diff --git a/apps/trigger/src/tasks/slack-agent-task/slack-agent-task.ts b/apps/trigger/src/tasks/slack-agent-task/slack-agent-task.ts index 763757eb7..9eef65371 100644 --- a/apps/trigger/src/tasks/slack-agent-task/slack-agent-task.ts +++ b/apps/trigger/src/tasks/slack-agent-task/slack-agent-task.ts @@ -1,4 +1,11 @@ -import { chats, db, eq, messages, updateMessage } from '@buster/database'; +import { + chats, + checkForDuplicateMessages, + db, + eq, + messages, + updateMessage, +} from '@buster/database'; import { SlackMessagingService, addReaction, @@ -244,6 +251,26 @@ export const slackAgentTask: ReturnType< prompt = `Please fulfill the request from this slack conversation:\n${formattedMessages}`; } + // Check for duplicate messages before creating + const duplicateCheck = await checkForDuplicateMessages({ + chatId: payload.chatId, + requestMessage: prompt, + }); + + if (duplicateCheck.isDuplicate) { + logger.warn('Duplicate message detected, stopping task', { + chatId: payload.chatId, + duplicateMessageIds: duplicateCheck.duplicateMessageIds, + requestMessage: prompt, + }); + + return { + success: false, + messageId: '', + triggerRunId: '', + }; + } + // Step 4: Create message const message = await createMessage({ chatId: payload.chatId, diff --git a/packages/database/src/queries/messages/messages.ts b/packages/database/src/queries/messages/messages.ts index e7a01f7e4..7994600dc 100644 --- a/packages/database/src/queries/messages/messages.ts +++ b/packages/database/src/queries/messages/messages.ts @@ -1,5 +1,5 @@ import type { InferSelectModel } from 'drizzle-orm'; -import { and, desc, eq, isNull } from 'drizzle-orm'; +import { and, desc, eq, isNull, ne } from 'drizzle-orm'; import { db } from '../../connection'; import { messages } from '../../schema'; @@ -233,3 +233,47 @@ export async function updateMessage( throw new Error(`Failed to update message ${messageId}`); } } + +/** + * Check for duplicate messages in the same chat + * @param options - Options for duplicate checking + * @returns Object indicating if duplicate exists and duplicate message IDs + */ +export async function checkForDuplicateMessages(options: { + chatId: string; + requestMessage: string; + excludeMessageId?: string; +}): Promise<{ isDuplicate: boolean; duplicateMessageIds: string[] }> { + const { chatId, requestMessage, excludeMessageId } = options; + + try { + // Build query conditions + const conditions = [ + eq(messages.chatId, chatId), + eq(messages.requestMessage, requestMessage), + isNull(messages.deletedAt), + ]; + + // Only add exclusion if messageId provided + if (excludeMessageId) { + conditions.push(ne(messages.id, excludeMessageId)); + } + + // Query for messages with same chatId and requestMessage + const duplicateMessages = await db + .select({ + id: messages.id, + }) + .from(messages) + .where(and(...conditions)) + .orderBy(desc(messages.createdAt)); + + return { + isDuplicate: duplicateMessages.length > 0, + duplicateMessageIds: duplicateMessages.map((msg) => msg.id), + }; + } catch (error) { + console.error('Failed to check for duplicate messages:', error); + throw new Error(`Failed to check for duplicate messages in chat ${chatId}`); + } +}