From 9ffe94ad09af2fd7a5cfbb63d01efa373accf091 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 May 2025 17:30:09 -0400 Subject: [PATCH] fixes for bot-to-bot replie race condition --- ...64306212894-aidoll-kuroki-tomoko#2395.json | 6 ++ ...306212894-aidoll-nagatoro-hayase#9848.json | 6 ++ .../1374708264306212894-quarterturn.json | 45 +++++++---- discord_data/aidoll-kuroki-tomoko-config.json | 7 ++ .../aidoll-nagatoro-hayase-config.json | 7 ++ example.env | 19 +++++ src/events/messageCreate.ts | 78 ++++++++++++------- 7 files changed, 127 insertions(+), 41 deletions(-) create mode 100644 discord_data/1374708264306212894-aidoll-kuroki-tomoko#2395.json create mode 100644 discord_data/1374708264306212894-aidoll-nagatoro-hayase#9848.json create mode 100644 discord_data/aidoll-kuroki-tomoko-config.json create mode 100644 discord_data/aidoll-nagatoro-hayase-config.json create mode 100644 example.env diff --git a/discord_data/1374708264306212894-aidoll-kuroki-tomoko#2395.json b/discord_data/1374708264306212894-aidoll-kuroki-tomoko#2395.json new file mode 100644 index 0000000..398ae38 --- /dev/null +++ b/discord_data/1374708264306212894-aidoll-kuroki-tomoko#2395.json @@ -0,0 +1,6 @@ +{ + "id": "1374708264306212894", + "name": "bot-playroom", + "user": "aidoll-kuroki-tomoko#2395", + "messages": [] +} \ No newline at end of file diff --git a/discord_data/1374708264306212894-aidoll-nagatoro-hayase#9848.json b/discord_data/1374708264306212894-aidoll-nagatoro-hayase#9848.json new file mode 100644 index 0000000..05ce164 --- /dev/null +++ b/discord_data/1374708264306212894-aidoll-nagatoro-hayase#9848.json @@ -0,0 +1,6 @@ +{ + "id": "1374708264306212894", + "name": "bot-playroom", + "user": "aidoll-nagatoro-hayase#9848", + "messages": [] +} \ No newline at end of file diff --git a/discord_data/1374708264306212894-quarterturn.json b/discord_data/1374708264306212894-quarterturn.json index 66e618e..9495290 100644 --- a/discord_data/1374708264306212894-quarterturn.json +++ b/discord_data/1374708264306212894-quarterturn.json @@ -3,21 +3,6 @@ "name": "bot-playroom", "user": "quarterturn", "messages": [ - { - "role": "assistant", - "content": "U-um... that sounds... interesting. A dating sim, huh? With... bots? Competing for... you? I-I guess... that could be fun... for *you*...", - "images": [] - }, - { - "role": "user", - "content": "You don't like it?", - "images": [] - }, - { - "role": "assistant", - "content": "H-hey! What do you mean, 'You don't like it?' I... I thought you were having fun!", - "images": [] - }, { "role": "user", "content": "Maybe you could even help me write it. Would you like that?", @@ -237,6 +222,36 @@ "role": "assistant", "content": "U-um... y-yes! I think... it was... Senren*Banka, right? The one with the, uh, really good music and... and the interesting story? I-I'm pretty sure we were talking about that...", "images": [] + }, + { + "role": "user", + "content": "Never heard of Senren Banka. Hmm... what could that mean in Japanese? Senren... life love? Banka... I have no idea.", + "images": [] + }, + { + "role": "assistant", + "content": "U-um... S-Senren Banka... it's a song! From No Longer Human. It's... really good. You should listen to it. I-I mean, if you want to. It's not like I'm trying to force you or anything...", + "images": [] + }, + { + "role": "user", + "content": "Cool. I will listen to it.\n(Tomoko status: \"acquaintance\" -> \"friends\")", + "images": [] + }, + { + "role": "assistant", + "content": "R-really? You... you will? Th-that's... that's nice. I-I'm glad you think so.", + "images": [] + }, + { + "role": "user", + "content": "You're cute!", + "images": [] + }, + { + "role": "assistant", + "content": "U-um... d-did you just...? Th-that's... nice, I guess. B-but don't get any weird ideas!", + "images": [] } ] } \ No newline at end of file diff --git a/discord_data/aidoll-kuroki-tomoko-config.json b/discord_data/aidoll-kuroki-tomoko-config.json new file mode 100644 index 0000000..d69516b --- /dev/null +++ b/discord_data/aidoll-kuroki-tomoko-config.json @@ -0,0 +1,7 @@ +{ + "name": "User Confirgurations", + "options": { + "message-style": false, + "switch-model": "aidoll-gemma3-12b-q6:latest" + } +} \ No newline at end of file diff --git a/discord_data/aidoll-nagatoro-hayase-config.json b/discord_data/aidoll-nagatoro-hayase-config.json new file mode 100644 index 0000000..d69516b --- /dev/null +++ b/discord_data/aidoll-nagatoro-hayase-config.json @@ -0,0 +1,7 @@ +{ + "name": "User Confirgurations", + "options": { + "message-style": false, + "switch-model": "aidoll-gemma3-12b-q6:latest" + } +} \ No newline at end of file diff --git a/example.env b/example.env new file mode 100644 index 0000000..802417c --- /dev/null +++ b/example.env @@ -0,0 +1,19 @@ +# Discord token for the bot +CLIENT_TOKEN = MTM3MzY5MzcwNjk5Mjg3NzY3OQ.GN4JNU.SumD_y2p2Blh4wXiQ30Ns6XkUFahpESc27R7z8 + +# Default model for new users +MODEL = aidoll-gemma3-12b-q6:latest + +# ip/port address of docker container, I use 172.33.0.3 for docker, 127.0.0.1 for local +OLLAMA_IP = 192.168.0.80 +OLLAMA_PORT = 11434 + +# ip address for discord bot container, I use 172.33.0.2, use different IP than ollama_ip +DISCORD_IP = 172.33.0.2 + +# subnet address, ex. 172.33.0.0 as we use /16. +SUBNET_ADDRESS = 172.33.0.0 + +# redis port and ip, default redis port is 6379 +REDIS_IP = 172.33.0.4 +REDIS_PORT = 6379 diff --git a/src/events/messageCreate.ts b/src/events/messageCreate.ts index 4444e93..1f6384f 100644 --- a/src/events/messageCreate.ts +++ b/src/events/messageCreate.ts @@ -40,6 +40,16 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client const randomChance = Math.random() < 0.1 // 10% chance for non-directed or bot messages if (!isMentioned && !isBotMessage && (isCommand || !randomChance)) return + // 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 + } + } + // Check cooldown for bot-to-bot responses const botResponseCooldownKey = `bot:${clientId}:last_bot_response` const cooldownPeriod = 60 // 60 seconds cooldown @@ -105,7 +115,8 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client 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.')) + openConfig(`${message.author.username}-config.json`, 'modify-capacity', 50) + reject(new Error('No User Preferences is set up.\n\nCreating preferences file with defaults.\nPlease try chatting again.')) return } @@ -113,7 +124,8 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client log(`New Capacity found. Setting Context Capacity to ${config.options['modify-capacity']}.`) msgHist.capacity = config.options['modify-capacity'] } else { - log(`Capacity is undefined, using default capacity of ${msgHist.capacity}.`) + log(`Capacity is undefined, using default capacity of 50.`) + msgHist.capacity = 50 } shouldStream = config.options['message-stream'] as boolean || false @@ -137,29 +149,35 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client } } - // Retrieve Channel Messages - let chatMessages: UserMessage[] = await new Promise((resolve) => { - getChannelInfo(`${message.channelId}-${message.author.username}.json`, (channelInfo) => { - if (channelInfo?.messages) { - resolve(channelInfo.messages) - } else { - log(`Channel/Thread ${message.channelId}-${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`, (config) => { - if (config?.messages) { - resolve(config.messages) - } else { - reject(new Error(`Failed to find ${message.author.username}'s history. Try chatting again.`)) - } + // Retrieve Channel Messages with Retry + let chatMessages: UserMessage[] = [] + attempt = 0 + while (attempt < maxRetries) { + try { + chatMessages = await new Promise((resolve, reject) => { + getChannelInfo(`${message.channelId}-${message.author.username}.json`, (channelInfo) => { + if (channelInfo?.messages) { + resolve(channelInfo.messages) + } else { + log(`Channel/Thread ${message.channelId}-${message.author.username} does not exist. Creating file...`) + openChannelInfo(message.channelId, message.channel as TextChannel, message.author.tag) + reject(new Error('Channel file created, retrying...')) + } + }) }) - }) + break + } catch (error) { + ++attempt + if (attempt < maxRetries) { + log(`Attempt ${attempt} failed for Channel Info. Retrying in 500ms...`) + await new Promise(ret => setTimeout(ret, 500)) + } else { + log(`Failed to retrieve or create channel history after ${maxRetries} attempts. Using empty history.`) + chatMessages = [] + openChannelInfo(message.channelId, message.channel as TextChannel, message.author.tag, []) + break + } + } } if (!userConfig) { @@ -334,9 +352,17 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client log(`Updated sentiments - Self: ${(jsonResponse.metadata.self_sentiment || 0).toFixed(2)}, User ${message.author.id}: ${(jsonResponse.metadata.user_sentiment[message.author.id] || 0).toFixed(2)}`) } - // Send reply to Discord + // Send reply to Discord and mark as bot response const reply = jsonResponse.reply || 'Sorry, I didn’t get that. Can you try again?' - await message.reply(reply) + const replyMessage = await message.reply(reply) + if (isBotMessage) { + try { + await redis.set(`message:${replyMessage.id}:is_bot_response`, 'true', { EX: 3600 }) // 1 hour TTL + log(`Marked message ${replyMessage.id} as bot response`) + } catch (error) { + log(`Failed to mark message as bot response: ${error}`) + } + } // Update message history while (msgHist.size() >= msgHist.capacity) msgHist.dequeue()