feat(tools): added support for ms teams attachments (#412)

* added support for attachments in teams

* ran linter

* fixed small error

* run lint

* removed comments

---------

Co-authored-by: Adam Gough <adamgough@Adams-MacBook-Pro.local>
This commit is contained in:
Adam Gough
2025-05-24 22:28:31 -07:00
committed by GitHub
co-authored by Adam Gough
parent 950e055a8d
commit 8ec194a604
6 changed files with 182 additions and 41 deletions
@@ -381,8 +381,8 @@ export function TeamsMessageSelector({
setSelectedChannelId(channel.channelId || '')
setSelectedChatId('')
setSelectedMessage(channel)
setSelectedMessageId(channel.id)
onChange(channel.id, channel)
setSelectedMessageId(channel.channelId || '')
onChange(channel.channelId || '', channel)
onMessageInfoChange?.(channel)
setOpen(false)
}
@@ -0,0 +1,29 @@
import type { MicrosoftTeamsAttachment } from './types'
/**
* Transform raw attachment data from Microsoft Graph API
*/
function transformAttachment(rawAttachment: any): MicrosoftTeamsAttachment {
return {
id: rawAttachment.id,
contentType: rawAttachment.contentType,
contentUrl: rawAttachment.contentUrl,
content: rawAttachment.content,
name: rawAttachment.name,
thumbnailUrl: rawAttachment.thumbnailUrl,
size: rawAttachment.size,
sourceUrl: rawAttachment.sourceUrl,
providerType: rawAttachment.providerType,
item: rawAttachment.item,
}
}
/**
* Extract attachments from message data
* Returns all attachments without any content processing
*/
export function extractMessageAttachments(message: any): MicrosoftTeamsAttachment[] {
const attachments = (message.attachments || []).map(transformAttachment)
return attachments
}
+81 -19
View File
@@ -1,11 +1,15 @@
import { createLogger } from '@/lib/logs/console-logger'
import type { ToolConfig } from '../types'
import { extractMessageAttachments } from './attachment-utils'
import type { MicrosoftTeamsReadResponse, MicrosoftTeamsToolParams } from './types'
const logger = createLogger('MicrosoftTeamsReadChannel')
export const readChannelTool: ToolConfig<MicrosoftTeamsToolParams, MicrosoftTeamsReadResponse> = {
id: 'microsoft_teams_read_channel',
name: 'Read Microsoft Teams Channel',
description: 'Read content from a Microsoft Teams channel',
version: '1.0',
version: '1.1',
oauth: {
required: true,
provider: 'microsoft-teams',
@@ -60,10 +64,16 @@ export const readChannelTool: ToolConfig<MicrosoftTeamsToolParams, MicrosoftTeam
}
},
},
transformResponse: async (response: Response) => {
transformResponse: async (response: Response, params?: MicrosoftTeamsToolParams) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Failed to read Microsoft Teams channel: ${errorText}`)
logger.error(
`Microsoft Teams channel API error: ${response.status} ${response.statusText}`,
errorText
)
throw new Error(
`Failed to read Microsoft Teams channel: ${response.status} ${response.statusText} - ${errorText}`
)
}
const data = await response.json()
@@ -81,36 +91,88 @@ export const readChannelTool: ToolConfig<MicrosoftTeamsToolParams, MicrosoftTeam
channelId: '',
messageCount: 0,
messages: [],
totalAttachments: 0,
attachmentTypes: [],
},
},
}
}
// Format the messages into a readable text
const formattedMessages = messages
.map((message: any) => {
if (!params?.teamId || !params?.channelId) {
throw new Error('Missing required parameters: teamId and channelId')
}
// Process messages with attachments
const processedMessages = messages.map((message: any, index: number) => {
try {
const content = message.body?.content || 'No content'
const sender = message.from?.user?.displayName || 'Unknown sender'
const timestamp = message.createdDateTime
? new Date(message.createdDateTime).toLocaleString()
const messageId = message.id
const attachments = extractMessageAttachments(message)
let sender = 'Unknown'
if (message.from?.user?.displayName) {
sender = message.from.user.displayName
} else if (message.messageType === 'systemEventMessage') {
sender = 'System'
}
return {
id: messageId,
content: content,
sender,
timestamp: message.createdDateTime,
messageType: message.messageType || 'message',
attachments,
}
} catch (error) {
logger.error(`Error processing message at index ${index}:`, error)
return {
id: message.id || `unknown-${index}`,
content: 'Error processing message',
sender: 'Unknown',
timestamp: message.createdDateTime || new Date().toISOString(),
messageType: 'error',
attachments: [],
}
}
})
// Format the messages into a readable text (no attachment info in content)
const formattedMessages = processedMessages
.map((message: any) => {
const sender = message.sender
const timestamp = message.timestamp
? new Date(message.timestamp).toLocaleString()
: 'Unknown time'
return `[${timestamp}] ${sender}: ${content}`
return `[${timestamp}] ${sender}: ${message.content}`
})
.join('\n\n')
// Calculate attachment statistics
const allAttachments = processedMessages.flatMap((msg: any) => msg.attachments || [])
const attachmentTypes: string[] = []
const seenTypes = new Set<string>()
allAttachments.forEach((att: any) => {
if (
att.contentType &&
typeof att.contentType === 'string' &&
!seenTypes.has(att.contentType)
) {
attachmentTypes.push(att.contentType)
seenTypes.add(att.contentType)
}
})
// Create document metadata
const metadata = {
teamId: messages[0]?.channelIdentity?.teamId || '',
channelId: messages[0]?.channelIdentity?.channelId || '',
teamId: messages[0]?.channelIdentity?.teamId || params.teamId || '',
channelId: messages[0]?.channelIdentity?.channelId || params.channelId || '',
messageCount: messages.length,
messages: messages.map((msg: any) => ({
id: msg.id,
content: msg.body?.content || '',
sender: msg.from?.user?.displayName || 'Unknown',
timestamp: msg.createdDateTime,
messageType: msg.messageType || 'message',
})),
totalAttachments: allAttachments.length,
attachmentTypes,
messages: processedMessages,
}
return {
+53 -17
View File
@@ -1,11 +1,12 @@
import type { ToolConfig } from '../types'
import { extractMessageAttachments } from './attachment-utils'
import type { MicrosoftTeamsReadResponse, MicrosoftTeamsToolParams } from './types'
export const readChatTool: ToolConfig<MicrosoftTeamsToolParams, MicrosoftTeamsReadResponse> = {
id: 'microsoft_teams_read_chat',
name: 'Read Microsoft Teams Chat',
description: 'Read content from a Microsoft Teams chat',
version: '1.0',
version: '1.1',
oauth: {
required: true,
provider: 'microsoft-teams',
@@ -44,7 +45,7 @@ export const readChatTool: ToolConfig<MicrosoftTeamsToolParams, MicrosoftTeamsRe
}
},
},
transformResponse: async (response: Response) => {
transformResponse: async (response: Response, params?: MicrosoftTeamsToolParams) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Failed to read Microsoft Teams chat: ${errorText}`)
@@ -64,35 +65,70 @@ export const readChatTool: ToolConfig<MicrosoftTeamsToolParams, MicrosoftTeamsRe
chatId: '',
messageCount: 0,
messages: [],
totalAttachments: 0,
attachmentTypes: [],
},
},
}
}
// Format the messages into a readable text
const formattedMessages = messages
if (!params?.chatId) {
throw new Error('Missing required parameter: chatId')
}
// Process messages with attachments
const processedMessages = messages.map((message: any) => {
const content = message.body?.content || 'No content'
const messageId = message.id
// Extract attachments without any content processing
const attachments = extractMessageAttachments(message)
return {
id: messageId,
content: content, // Keep original content without modification
sender: message.from?.user?.displayName || 'Unknown',
timestamp: message.createdDateTime,
messageType: message.messageType || 'message',
attachments, // Attachments only stored here
}
})
// Format the messages into a readable text (no attachment info in content)
const formattedMessages = processedMessages
.map((message: any) => {
const content = message.body?.content || 'No content'
const sender = message.from?.user?.displayName || 'Unknown sender'
const timestamp = message.createdDateTime
? new Date(message.createdDateTime).toLocaleString()
const sender = message.sender
const timestamp = message.timestamp
? new Date(message.timestamp).toLocaleString()
: 'Unknown time'
return `[${timestamp}] ${sender}: ${content}`
return `[${timestamp}] ${sender}: ${message.content}`
})
.join('\n\n')
// Calculate attachment statistics
const allAttachments = processedMessages.flatMap((msg: any) => msg.attachments || [])
const attachmentTypes: string[] = []
const seenTypes = new Set<string>()
allAttachments.forEach((att: any) => {
if (
att.contentType &&
typeof att.contentType === 'string' &&
!seenTypes.has(att.contentType)
) {
attachmentTypes.push(att.contentType)
seenTypes.add(att.contentType)
}
})
// Create document metadata
const metadata = {
chatId: messages[0]?.chatId || '',
chatId: messages[0]?.chatId || params.chatId || '',
messageCount: messages.length,
messages: messages.map((msg: any) => ({
id: msg.id,
content: msg.body?.content || '',
sender: msg.from?.user?.displayName || 'Unknown',
timestamp: msg.createdDateTime,
messageType: msg.messageType || 'message',
})),
totalAttachments: allAttachments.length,
attachmentTypes,
messages: processedMessages,
}
return {
+17 -1
View File
@@ -1,5 +1,18 @@
import type { ToolResponse } from '../types'
export interface MicrosoftTeamsAttachment {
id: string
contentType: string
contentUrl?: string
content?: string
name?: string
thumbnailUrl?: string
size?: number
sourceUrl?: string
providerType?: string
item?: any
}
export interface MicrosoftTeamsMetadata {
messageId?: string
channelId?: string
@@ -7,7 +20,6 @@ export interface MicrosoftTeamsMetadata {
chatId?: string
content?: string
createdTime?: string
modifiedTime?: string
url?: string
messageCount?: number
messages?: Array<{
@@ -16,7 +28,11 @@ export interface MicrosoftTeamsMetadata {
sender: string
timestamp: string
messageType: string
attachments?: MicrosoftTeamsAttachment[]
}>
// Global attachments summary
totalAttachments?: number
attachmentTypes?: string[]
}
export interface MicrosoftTeamsReadResponse extends ToolResponse {
@@ -44,11 +44,9 @@ export const writeChannelTool: ToolConfig<MicrosoftTeamsToolParams, MicrosoftTea
throw new Error('Channel ID is required')
}
// URL encode the IDs to handle special characters
const encodedTeamId = encodeURIComponent(teamId)
const encodedChannelId = encodeURIComponent(channelId)
// Send a message to a channel
const url = `https://graph.microsoft.com/v1.0/teams/${encodedTeamId}/channels/${encodedChannelId}/messages`
return url