diff --git a/src/lib/discord-bot.ts b/src/lib/discord-bot.ts index b16efaf7b..7bef2825a 100644 --- a/src/lib/discord-bot.ts +++ b/src/lib/discord-bot.ts @@ -11,7 +11,7 @@ import { import type OpenAI from 'openai'; import type { Owner } from '@/lib/integrations/core/types'; import { - getInstallationByGuildId, + getAllInstallationsByGuildId, getOwnerFromInstallation, getModel, } from '@/lib/integrations/discord-service'; @@ -220,8 +220,9 @@ export async function processDiscordBotMessage( let cloudAgentSessionId: string | undefined; - const installation = await getInstallationByGuildId(guildId); - if (!installation) { + // Look up all Discord integrations for this guild to detect duplicates + const allInstallations = await getAllInstallationsByGuildId(guildId); + if (allInstallations.length === 0) { return { response: 'Error: No Discord integration found for this server. Please install the Kilo Code Discord integration.', @@ -232,6 +233,28 @@ export async function processDiscordBotMessage( }; } + if (allInstallations.length > 1) { + const ownerIds = allInstallations.map( + i => i.owned_by_organization_id ?? i.owned_by_user_id ?? 'unknown' + ); + console.warn( + '[DiscordBot] Multiple Discord integrations found for guild:', + guildId, + 'owners:', + ownerIds + ); + return { + response: + 'Multiple integrations found for this message. Please remove duplicate Discord integrations from your Kilo account to resolve this issue.', + modelUsed: '', + toolCallsMade: [], + error: `Multiple Discord integrations found for guild ${guildId}`, + installation: null, + }; + } + + const [installation] = allInstallations; + const owner = getOwnerFromInstallation(installation); if (!owner) { return { diff --git a/src/lib/integrations/discord-service.ts b/src/lib/integrations/discord-service.ts index d0909d2a5..1e6e4f006 100644 --- a/src/lib/integrations/discord-service.ts +++ b/src/lib/integrations/discord-service.ts @@ -150,6 +150,24 @@ export async function getInstallationByGuildId( return integration || null; } +/** + * Get all Discord installations for a given guild ID. + * Used to detect duplicate integrations for the same Discord server. + */ +export async function getAllInstallationsByGuildId( + guildId: string +): Promise { + return db + .select() + .from(platform_integrations) + .where( + and( + eq(platform_integrations.platform, PLATFORM.DISCORD), + eq(platform_integrations.platform_installation_id, guildId) + ) + ); +} + /** * Get the owner information from a Discord installation */ diff --git a/src/lib/integrations/slack-service.ts b/src/lib/integrations/slack-service.ts index c5e157eaf..66c3719da 100644 --- a/src/lib/integrations/slack-service.ts +++ b/src/lib/integrations/slack-service.ts @@ -157,6 +157,24 @@ export async function getInstallationByTeamId(teamId: string): Promise { + return db + .select() + .from(platform_integrations) + .where( + and( + eq(platform_integrations.platform, PLATFORM.SLACK), + eq(platform_integrations.platform_installation_id, teamId) + ) + ); +} + /** * Get the owner information from a Slack installation */ diff --git a/src/lib/slack-bot.ts b/src/lib/slack-bot.ts index 1743cfc86..f31d32c48 100644 --- a/src/lib/slack-bot.ts +++ b/src/lib/slack-bot.ts @@ -17,7 +17,7 @@ import { import type OpenAI from 'openai'; import type { Owner } from '@/lib/integrations/core/types'; import { - getInstallationByTeamId, + getAllInstallationsByTeamId, getOwnerFromInstallation, getModel, getAccessTokenFromInstallation, @@ -387,9 +387,9 @@ export async function processKiloBotMessage( // Track metadata for logging let cloudAgentSessionId: string | undefined; - // Look up the Slack integration to find the owner - const installation = await getInstallationByTeamId(teamId); - if (!installation) { + // Look up all Slack integrations for this team to detect duplicates + const allInstallations = await getAllInstallationsByTeamId(teamId); + if (allInstallations.length === 0) { console.error('[SlackBot] No Slack installation found for team:', teamId); return { response: @@ -401,6 +401,28 @@ export async function processKiloBotMessage( }; } + if (allInstallations.length > 1) { + const ownerIds = allInstallations.map( + i => i.owned_by_organization_id ?? i.owned_by_user_id ?? 'unknown' + ); + console.warn( + '[SlackBot] Multiple Slack integrations found for team:', + teamId, + 'owners:', + ownerIds + ); + return { + response: + 'Multiple integrations found for this message. Please remove duplicate Slack integrations from your Kilo account to resolve this issue.', + modelUsed: '', + toolCallsMade: [], + error: `Multiple Slack integrations found for team ${teamId}`, + installation: null, + }; + } + + const [installation] = allInstallations; + const owner = getOwnerFromInstallation(installation); if (!owner) { console.error('[SlackBot] Could not determine owner from installation:', installation.id);