diff --git a/apps/sim/app/api/tools/microsoft_teams/write_channel/route.ts b/apps/sim/app/api/tools/microsoft_teams/write_channel/route.ts index fad1296281..c014541a44 100644 --- a/apps/sim/app/api/tools/microsoft_teams/write_channel/route.ts +++ b/apps/sim/app/api/tools/microsoft_teams/write_channel/route.ts @@ -4,7 +4,7 @@ import { checkHybridAuth } from '@/lib/auth/hybrid' import { createLogger } from '@/lib/logs/console/logger' import { downloadFileFromStorage, processFilesToUserFiles } from '@/lib/uploads/file-processing' import { generateRequestId } from '@/lib/utils' -import { resolveMentionsForChannel } from '@/tools/microsoft_teams/utils' +import { resolveMentionsForChannel, type TeamsMention } from '@/tools/microsoft_teams/utils' export const dynamic = 'force-dynamic' @@ -142,11 +142,7 @@ export async function POST(request: NextRequest) { let messageContent = validatedData.content let contentType: 'text' | 'html' = 'text' - const mentionEntities: Array<{ - type: 'mention' - mentioned: { id: string; name: string } - text: string - }> = [] + const mentionEntities: TeamsMention[] = [] try { const mentionResult = await resolveMentionsForChannel( @@ -158,8 +154,9 @@ export async function POST(request: NextRequest) { if (mentionResult.hasMentions) { contentType = 'html' - mentionEntities.push(...mentionResult.entities) - logger.info(`[${requestId}] Resolved ${mentionResult.entities.length} mention(s)`) + messageContent = mentionResult.updatedContent + mentionEntities.push(...mentionResult.mentions) + logger.info(`[${requestId}] Resolved ${mentionResult.mentions.length} mention(s)`) } } catch (error) { logger.warn(`[${requestId}] Failed to resolve mentions, continuing without them:`, error) @@ -170,7 +167,7 @@ export async function POST(request: NextRequest) { const attachmentTags = attachments .map((att) => ``) .join(' ') - messageContent = `${validatedData.content}
${attachmentTags}` + messageContent = `${messageContent}
${attachmentTags}` } const messageBody: { @@ -179,11 +176,7 @@ export async function POST(request: NextRequest) { content: string } attachments?: any[] - mentions?: Array<{ - type: 'mention' - mentioned: { id: string; name: string } - text: string - }> + mentions?: TeamsMention[] } = { body: { contentType, diff --git a/apps/sim/app/api/tools/microsoft_teams/write_chat/route.ts b/apps/sim/app/api/tools/microsoft_teams/write_chat/route.ts index 445edf480c..f0156e5502 100644 --- a/apps/sim/app/api/tools/microsoft_teams/write_chat/route.ts +++ b/apps/sim/app/api/tools/microsoft_teams/write_chat/route.ts @@ -4,7 +4,7 @@ import { checkHybridAuth } from '@/lib/auth/hybrid' import { createLogger } from '@/lib/logs/console/logger' import { downloadFileFromStorage, processFilesToUserFiles } from '@/lib/uploads/file-processing' import { generateRequestId } from '@/lib/utils' -import { resolveMentionsForChat } from '@/tools/microsoft_teams/utils' +import { resolveMentionsForChat, type TeamsMention } from '@/tools/microsoft_teams/utils' export const dynamic = 'force-dynamic' @@ -140,11 +140,7 @@ export async function POST(request: NextRequest) { let messageContent = validatedData.content let contentType: 'text' | 'html' = 'text' - const mentionEntities: Array<{ - type: 'mention' - mentioned: { id: string; name: string } - text: string - }> = [] + const mentionEntities: TeamsMention[] = [] try { const mentionResult = await resolveMentionsForChat( @@ -155,8 +151,9 @@ export async function POST(request: NextRequest) { if (mentionResult.hasMentions) { contentType = 'html' - mentionEntities.push(...mentionResult.entities) - logger.info(`[${requestId}] Resolved ${mentionResult.entities.length} mention(s)`) + messageContent = mentionResult.updatedContent + mentionEntities.push(...mentionResult.mentions) + logger.info(`[${requestId}] Resolved ${mentionResult.mentions.length} mention(s)`) } } catch (error) { logger.warn(`[${requestId}] Failed to resolve mentions, continuing without them:`, error) @@ -167,7 +164,7 @@ export async function POST(request: NextRequest) { const attachmentTags = attachments .map((att) => ``) .join(' ') - messageContent = `${validatedData.content}
${attachmentTags}` + messageContent = `${messageContent}
${attachmentTags}` } const messageBody: { @@ -176,11 +173,7 @@ export async function POST(request: NextRequest) { content: string } attachments?: any[] - mentions?: Array<{ - type: 'mention' - mentioned: { id: string; name: string } - text: string - }> + mentions?: TeamsMention[] } = { body: { contentType, diff --git a/apps/sim/tools/microsoft_teams/utils.ts b/apps/sim/tools/microsoft_teams/utils.ts index 20b6939318..e8b03d8a53 100644 --- a/apps/sim/tools/microsoft_teams/utils.ts +++ b/apps/sim/tools/microsoft_teams/utils.ts @@ -7,8 +7,7 @@ const logger = createLogger('MicrosoftTeamsUtils') interface ParsedMention { name: string fullTag: string - startIndex: number - endIndex: number + mentionId: number } interface TeamMember { @@ -17,13 +16,22 @@ interface TeamMember { userIdentityType?: string } -export interface MentionEntity { - type: 'mention' - mentioned: { - id: string - name: string - } - text: string +export interface TeamsMention { + id: number + mentionText: string + mentioned: + | { + id: string + name: string + userIdentityType?: string + } + | { + application: { + displayName: string + id: string + applicationIdentityType: 'bot' + } + } } /** @@ -126,6 +134,7 @@ function parseMentions(content: string): ParsedMention[] { const mentions: ParsedMention[] = [] const mentionRegex = /([^<]+)<\/at>/gi let match: RegExpExecArray | null + let mentionId = 0 while ((match = mentionRegex.exec(content)) !== null) { const name = match[1].trim() @@ -133,8 +142,7 @@ function parseMentions(content: string): ParsedMention[] { mentions.push({ name, fullTag: match[0], - startIndex: match.index, - endIndex: match.index + match[0].length, + mentionId: mentionId++, }) } } @@ -201,40 +209,63 @@ export async function resolveMentionsForChat( content: string, chatId: string, accessToken: string -): Promise<{ entities: MentionEntity[]; hasMentions: boolean }> { +): Promise<{ mentions: TeamsMention[]; hasMentions: boolean; updatedContent: string }> { const parsedMentions = parseMentions(content) if (parsedMentions.length === 0) { - return { entities: [], hasMentions: false } + return { mentions: [], hasMentions: false, updatedContent: content } } const members = await fetchChatMembers(chatId, accessToken) - const entities: MentionEntity[] = [] - const resolvedNames = new Set() + const mentions: TeamsMention[] = [] + const resolvedTags = new Set() + let updatedContent = content for (const mention of parsedMentions) { - if (resolvedNames.has(mention.fullTag)) { + if (resolvedTags.has(mention.fullTag)) { continue } const member = findMemberByName(members, mention.name) if (member) { - entities.push({ - type: 'mention', - mentioned: { - id: member.id, - name: member.displayName, - }, - text: mention.fullTag, - }) - resolvedNames.add(mention.fullTag) + const isBot = member.userIdentityType === 'bot' + + if (isBot) { + mentions.push({ + id: mention.mentionId, + mentionText: mention.name, + mentioned: { + application: { + displayName: member.displayName, + id: member.id, + applicationIdentityType: 'bot', + }, + }, + }) + } else { + mentions.push({ + id: mention.mentionId, + mentionText: mention.name, + mentioned: { + id: member.id, + name: member.displayName, + userIdentityType: member.userIdentityType, + }, + }) + } + resolvedTags.add(mention.fullTag) + updatedContent = updatedContent.replace( + mention.fullTag, + `${mention.name}` + ) } } return { - entities, - hasMentions: entities.length > 0, + mentions, + hasMentions: mentions.length > 0, + updatedContent, } } @@ -243,39 +274,62 @@ export async function resolveMentionsForChannel( teamId: string, channelId: string, accessToken: string -): Promise<{ entities: MentionEntity[]; hasMentions: boolean }> { +): Promise<{ mentions: TeamsMention[]; hasMentions: boolean; updatedContent: string }> { const parsedMentions = parseMentions(content) if (parsedMentions.length === 0) { - return { entities: [], hasMentions: false } + return { mentions: [], hasMentions: false, updatedContent: content } } const members = await fetchChannelMembers(teamId, channelId, accessToken) - const entities: MentionEntity[] = [] - const resolvedNames = new Set() + const mentions: TeamsMention[] = [] + const resolvedTags = new Set() + let updatedContent = content for (const mention of parsedMentions) { - if (resolvedNames.has(mention.fullTag)) { + if (resolvedTags.has(mention.fullTag)) { continue } const member = findMemberByName(members, mention.name) if (member) { - entities.push({ - type: 'mention', - mentioned: { - id: member.id, - name: member.displayName, - }, - text: mention.fullTag, - }) - resolvedNames.add(mention.fullTag) + const isBot = member.userIdentityType === 'bot' + + if (isBot) { + mentions.push({ + id: mention.mentionId, + mentionText: mention.name, + mentioned: { + application: { + displayName: member.displayName, + id: member.id, + applicationIdentityType: 'bot', + }, + }, + }) + } else { + mentions.push({ + id: mention.mentionId, + mentionText: mention.name, + mentioned: { + id: member.id, + name: member.displayName, + userIdentityType: member.userIdentityType, + }, + }) + } + resolvedTags.add(mention.fullTag) + updatedContent = updatedContent.replace( + mention.fullTag, + `${mention.name}` + ) } } return { - entities, - hasMentions: entities.length > 0, + mentions, + hasMentions: mentions.length > 0, + updatedContent, } }