fix errors with no config found for new bot
This commit is contained in:
@@ -54,10 +54,14 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client
|
||||
// Check if message is a bot response to avoid loops
|
||||
const isBotResponseKey = `message:${message.id}:is_bot_response`
|
||||
if (isBotMessage) {
|
||||
const isBotResponse = await redis.get(isBotResponseKey)
|
||||
if (isBotResponse === 'true') {
|
||||
log(`Skipping bot message ${message.id} as it is a bot response.`)
|
||||
return
|
||||
try {
|
||||
const isBotResponse = await redis.get(isBotResponseKey)
|
||||
if (isBotResponse === 'true') {
|
||||
log(`Skipping bot message ${message.id} as it is a bot response.`)
|
||||
return
|
||||
}
|
||||
} catch (error) {
|
||||
log(`Failed to check is_bot_response: ${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,16 +114,18 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client
|
||||
try {
|
||||
// Retrieve Server/Guild Preferences
|
||||
let attempt = 0
|
||||
let serverConfig: ServerConfig | undefined
|
||||
while (attempt < maxRetries) {
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
serverConfig = await new Promise((resolve, reject) => {
|
||||
getServerConfig(`${message.guildId}-config.json`, (config) => {
|
||||
if (config === undefined) {
|
||||
redis.set(`server:${message.guildId}:config`, JSON.stringify({ options: { 'toggle-chat': true } }))
|
||||
reject(new Error('Failed to locate or create Server Preferences\n\nPlease try chatting again...'))
|
||||
if (!config) {
|
||||
reject(new Error('Failed to retrieve or create Server Preferences'))
|
||||
} else if (!config.options['toggle-chat']) {
|
||||
reject(new Error('Admin(s) have disabled chat features.\n\nPlease contact your server\'s admin(s).'))
|
||||
} else {
|
||||
// Sync with Redis
|
||||
redis.set(`server:${message.guildId}:config`, JSON.stringify(config)).catch((err) => log(`Failed to sync server config to Redis: ${err}`))
|
||||
resolve(config)
|
||||
}
|
||||
})
|
||||
@@ -131,7 +137,21 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client
|
||||
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...`)
|
||||
// Check Redis for server config as fallback
|
||||
try {
|
||||
const redisConfig = await redis.get(`server:${message.guildId}:config`)
|
||||
if (redisConfig) {
|
||||
serverConfig = JSON.parse(redisConfig)
|
||||
if (serverConfig.options['toggle-chat']) {
|
||||
break
|
||||
} else {
|
||||
throw new Error('Admin(s) have disabled chat features.\n\nPlease contact your server\'s admin(s).'))
|
||||
}
|
||||
}
|
||||
} catch (redisError) {
|
||||
log(`Redis fallback failed: ${redisError}`)
|
||||
}
|
||||
throw new Error('Could not retrieve Server Preferences, please try chatting again...')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,7 +325,7 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client
|
||||
: `Responding to user ${message.author.tag}`
|
||||
|
||||
// Construct prompt with [CHARACTER], [SENTIMENT], and [CONTEXT]
|
||||
const prompt = `[CHARACTER]\n${personality}\n[SENTIMENT]\n${sentimentData}\n[CONTEXT]\n${messageContext}\n[USER_INPUT]\n${cleanedMessage}`
|
||||
const prompt = `[CHARACTER]\n${personality}\n[SENTIMENT]\n${sentimentData}\n[CONTEXT]\n${messageContext}\n[USER_INPUT]\n${cleanedMessage}\n[INSTRUCTION]\nRespond in JSON format with keys: status, reply, metadata. Example: {"status":"success","reply":"Hi!","metadata":{}}`
|
||||
|
||||
// Set up message history queue
|
||||
msgHist.setQueue(chatMessages)
|
||||
@@ -333,6 +353,9 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client
|
||||
// Strip Markdown code fences if present
|
||||
let content = response.message.content
|
||||
content = content.replace(/^```json\n|```$/g, '').trim()
|
||||
if (!content.startsWith('{') || !content.endsWith('}')) {
|
||||
throw new Error('Model response is not valid JSON')
|
||||
}
|
||||
jsonResponse = JSON.parse(content)
|
||||
if (!jsonResponse.status || !jsonResponse.reply) {
|
||||
throw new Error('Missing status or reply in model response')
|
||||
|
||||
@@ -1,67 +1,91 @@
|
||||
import { ChannelType } from 'discord.js'
|
||||
import { UserMessage } from './index.js'
|
||||
|
||||
export interface UserConfiguration {
|
||||
'message-stream'?: boolean,
|
||||
'modify-capacity': number,
|
||||
'switch-model': string
|
||||
}
|
||||
|
||||
export interface ServerConfiguration {
|
||||
'toggle-chat'?: boolean,
|
||||
}
|
||||
import { Configuration, ServerConfig, UserConfig, isServerConfigurationKey } from '../index.js'
|
||||
import fs from 'fs/promises' // Use promises for async
|
||||
import path from 'path'
|
||||
|
||||
/**
|
||||
* Parent Configuration interface
|
||||
* Method to open a file in the working directory and modify/create it
|
||||
*
|
||||
* @see ServerConfiguration server settings per guild
|
||||
* @see UserConfiguration user configurations (only for the user for any server)
|
||||
* @param filename name of the file
|
||||
* @param key key value to access
|
||||
* @param value new value to assign
|
||||
*/
|
||||
export interface Configuration {
|
||||
readonly name: string
|
||||
options: UserConfiguration | ServerConfiguration
|
||||
export async function openConfig(filename: string, key: string, value: any): Promise<void> {
|
||||
const fullFileName = `data/${filename}`
|
||||
|
||||
let object: Configuration;
|
||||
try {
|
||||
if (await fs.access(fullFileName).then(() => true).catch(() => false)) {
|
||||
const data = await fs.readFile(fullFileName, 'utf8');
|
||||
object = JSON.parse(data);
|
||||
object['options'][key] = value;
|
||||
} else {
|
||||
// Create new config
|
||||
object = {
|
||||
name: isServerConfigurationKey(key) ? "Server Configurations" : "User Configurations",
|
||||
options: { [key]: value }
|
||||
};
|
||||
|
||||
const directory = path.dirname(fullFileName);
|
||||
await fs.mkdir(directory, { recursive: true });
|
||||
}
|
||||
|
||||
await fs.writeFile(fullFileName, JSON.stringify(object, null, 2));
|
||||
console.log(`[Util: openConfig] Updated/Created '${filename}' in working directory`);
|
||||
} catch (error) {
|
||||
console.error(`[Error: openConfig] Failed to process config file: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User config to use outside of this file
|
||||
* Method to obtain the configurations of the message chat/thread
|
||||
*
|
||||
* @param filename name of the configuration file to get
|
||||
* @param callback function to allow a promise from getting the config
|
||||
*/
|
||||
export interface UserConfig {
|
||||
readonly name: string
|
||||
options: UserConfiguration
|
||||
}
|
||||
export async function getServerConfig(filename: string, callback: (config: ServerConfig | undefined) => void): Promise<void> {
|
||||
const fullFileName = `data/${filename}`;
|
||||
|
||||
export interface ServerConfig {
|
||||
readonly name: string
|
||||
options: ServerConfiguration
|
||||
}
|
||||
|
||||
export interface Channel {
|
||||
readonly id: string
|
||||
readonly name: string
|
||||
readonly user: string
|
||||
messages: UserMessage[]
|
||||
try {
|
||||
if (await fs.access(fullFileName).then(() => true).catch(() => false)) {
|
||||
const data = await fs.readFile(fullFileName, 'utf8');
|
||||
callback(JSON.parse(data));
|
||||
} else {
|
||||
// Create default server config
|
||||
const defaultConfig: ServerConfig = {
|
||||
name: "Server Configurations",
|
||||
options: { 'toggle-chat': true }
|
||||
};
|
||||
const directory = path.dirname(fullFileName);
|
||||
await fs.mkdir(directory, { recursive: true });
|
||||
await fs.writeFile(fullFileName, JSON.stringify(defaultConfig, null, 2));
|
||||
console.log(`[Util: getServerConfig] Created default config '${filename}'`);
|
||||
callback(defaultConfig);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[Error: getServerConfig] Failed to read or create config: ${error}`);
|
||||
callback(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The following 2 types is allow for better readability in commands
|
||||
* Admin Command -> Don't run in Threads
|
||||
* User Command -> Used anywhere
|
||||
* Method to obtain the configurations of the message chat/thread
|
||||
*
|
||||
* @param filename name of the configuration file to get
|
||||
* @param callback function to allow a promise from getting the config
|
||||
*/
|
||||
export const AdminCommand = [
|
||||
ChannelType.GuildText
|
||||
]
|
||||
export async function getUserConfig(filename: string, callback: (config: UserConfig | undefined) => void): Promise<void> {
|
||||
const fullFileName = `data/${filename}`;
|
||||
|
||||
export const UserCommand = [
|
||||
ChannelType.GuildText,
|
||||
ChannelType.PublicThread,
|
||||
ChannelType.PrivateThread
|
||||
]
|
||||
|
||||
/**
|
||||
* Check if the configuration we are editing/taking from is a Server Config
|
||||
* @param key name of command we ran
|
||||
* @returns true if command is from Server Config, false otherwise
|
||||
*/
|
||||
export function isServerConfigurationKey(key: string): key is keyof ServerConfiguration {
|
||||
return ['toggle-chat'].includes(key);
|
||||
}
|
||||
try {
|
||||
if (await fs.access(fullFileName).then(() => true).catch(() => false)) {
|
||||
const data = await fs.readFile(fullFileName, 'utf8');
|
||||
callback(JSON.parse(data));
|
||||
} else {
|
||||
callback(undefined); // User config handled by Redis in messageCreate.ts
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[Error: getUserConfig] Failed to read config: ${error}`);
|
||||
callback(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user