mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix(integrations): gdrive trashed search, slack blocks-with-file, slack get_message ts (#4600)
* fix(integrations): gdrive trashed search, slack blocks-with-file, slack get_message ts - Google Drive search/list: skip default `trashed = false` when user query already specifies a `trashed = ...` predicate, so trashed-file searches work. - Slack send-message with files: forward `blocks` through to `files.completeUploadExternal` so Block Kit renders when files are attached. - Slack get_message: switch from `conversations.history` (oldest lower-bound returned the next message after) to `conversations.replies` with `ts=` for exact-match lookup, plus a defensive ts-equality guard and clearer error. * fix(google_drive): revert list.ts trashed guard — query is plain text, not gdrive syntax * fix(slack): omit initial_comment when blocks present so Block Kit actually renders on file uploads
This commit is contained in:
@@ -141,7 +141,8 @@ async function completeSlackFileUpload(
|
||||
channel: string,
|
||||
text: string,
|
||||
accessToken: string,
|
||||
threadTs?: string | null
|
||||
threadTs?: string | null,
|
||||
blocks?: unknown[] | null
|
||||
): Promise<{ ok: boolean; files?: any[]; error?: string }> {
|
||||
const response = await fetch('https://slack.com/api/files.completeUploadExternal', {
|
||||
method: 'POST',
|
||||
@@ -152,7 +153,10 @@ async function completeSlackFileUpload(
|
||||
body: JSON.stringify({
|
||||
files: uploadedFileIds.map((id) => ({ id })),
|
||||
channel_id: channel,
|
||||
initial_comment: text,
|
||||
// Per Slack docs for files.completeUploadExternal: if `initial_comment`
|
||||
// is provided, `blocks` is silently ignored. So when blocks are present
|
||||
// we omit initial_comment and let blocks render instead.
|
||||
...(blocks && blocks.length > 0 ? { blocks } : { initial_comment: text }),
|
||||
...(threadTs && { thread_ts: threadTs }),
|
||||
}),
|
||||
})
|
||||
@@ -295,7 +299,14 @@ export async function sendSlackMessage(
|
||||
}
|
||||
|
||||
// Complete file upload with thread support
|
||||
const completeData = await completeSlackFileUpload(fileIds, channel, text, accessToken, threadTs)
|
||||
const completeData = await completeSlackFileUpload(
|
||||
fileIds,
|
||||
channel,
|
||||
text,
|
||||
accessToken,
|
||||
threadTs,
|
||||
blocks
|
||||
)
|
||||
|
||||
if (!completeData.ok) {
|
||||
logger.error(`[${requestId}] Failed to complete upload:`, completeData.error)
|
||||
|
||||
@@ -66,8 +66,11 @@ export const listTool: ToolConfig<GoogleDriveToolParams, GoogleDriveListResponse
|
||||
const escapeQueryValue = (value: string): string =>
|
||||
value.replace(/\\/g, '\\\\').replace(/'/g, "\\'")
|
||||
|
||||
// Build the query conditions
|
||||
const conditions = ['trashed = false'] // Always exclude trashed files
|
||||
// Build the query conditions. `params.query` here is a plain-text name
|
||||
// search term (wrapped in `name contains '...'` below), not Google Drive
|
||||
// query syntax — so there's no caller-supplied `trashed` predicate to
|
||||
// honour. Always exclude trashed files.
|
||||
const conditions: string[] = ['trashed = false']
|
||||
const folderId = (params.folderId || params.folderSelector)?.trim()
|
||||
if (folderId) {
|
||||
const escapedFolderId = escapeQueryValue(folderId)
|
||||
|
||||
@@ -64,9 +64,14 @@ export const searchTool: ToolConfig<GoogleDriveSearchParams, GoogleDriveSearchRe
|
||||
url.searchParams.append('includeItemsFromAllDrives', 'true')
|
||||
|
||||
// The query is passed directly as Google Drive query syntax
|
||||
const conditions = ['trashed = false']
|
||||
if (params.query?.trim()) {
|
||||
conditions.push(params.query.trim())
|
||||
const userQuery = params.query?.trim()
|
||||
const userSpecifiesTrashed = userQuery ? /\btrashed\s*=/.test(userQuery) : false
|
||||
const conditions: string[] = []
|
||||
if (!userSpecifiesTrashed) {
|
||||
conditions.push('trashed = false')
|
||||
}
|
||||
if (userQuery) {
|
||||
conditions.push(userQuery)
|
||||
}
|
||||
url.searchParams.append('q', conditions.join(' and '))
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { SlackGetMessageParams, SlackGetMessageResponse } from '@/tools/slack/types'
|
||||
import { MESSAGE_OUTPUT_PROPERTIES } from '@/tools/slack/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('SlackGetMessageTool')
|
||||
|
||||
export const slackGetMessageTool: ToolConfig<SlackGetMessageParams, SlackGetMessageResponse> = {
|
||||
id: 'slack_get_message',
|
||||
name: 'Slack Get Message',
|
||||
@@ -49,11 +52,10 @@ export const slackGetMessageTool: ToolConfig<SlackGetMessageParams, SlackGetMess
|
||||
|
||||
request: {
|
||||
url: (params: SlackGetMessageParams) => {
|
||||
const url = new URL('https://slack.com/api/conversations.history')
|
||||
const url = new URL('https://slack.com/api/conversations.replies')
|
||||
url.searchParams.append('channel', params.channel?.trim() ?? '')
|
||||
url.searchParams.append('oldest', params.timestamp?.trim() ?? '')
|
||||
url.searchParams.append('ts', params.timestamp?.trim() ?? '')
|
||||
url.searchParams.append('limit', '1')
|
||||
url.searchParams.append('inclusive', 'true')
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
@@ -63,8 +65,9 @@ export const slackGetMessageTool: ToolConfig<SlackGetMessageParams, SlackGetMess
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
transformResponse: async (response: Response, params?: SlackGetMessageParams) => {
|
||||
const data = await response.json()
|
||||
const requestedTs = params?.timestamp?.trim() ?? ''
|
||||
|
||||
if (!data.ok) {
|
||||
if (data.error === 'missing_scope') {
|
||||
@@ -78,15 +81,25 @@ export const slackGetMessageTool: ToolConfig<SlackGetMessageParams, SlackGetMess
|
||||
if (data.error === 'channel_not_found') {
|
||||
throw new Error('Channel not found. Please check the channel ID.')
|
||||
}
|
||||
if (data.error === 'message_not_found' || data.error === 'thread_not_found') {
|
||||
throw new Error(`Message not found at timestamp ${requestedTs}`)
|
||||
}
|
||||
throw new Error(data.error || 'Failed to get message from Slack')
|
||||
}
|
||||
|
||||
const messages = data.messages || []
|
||||
if (messages.length === 0) {
|
||||
throw new Error('Message not found')
|
||||
throw new Error(`Message not found at timestamp ${requestedTs}`)
|
||||
}
|
||||
|
||||
const msg = messages[0]
|
||||
if (requestedTs && msg.ts !== requestedTs) {
|
||||
logger.warn('Slack returned a message with a different timestamp than requested', {
|
||||
requestedTs,
|
||||
returnedTs: msg.ts,
|
||||
})
|
||||
throw new Error(`Message not found at timestamp ${requestedTs}`)
|
||||
}
|
||||
const message = {
|
||||
type: msg.type ?? 'message',
|
||||
ts: msg.ts,
|
||||
|
||||
Reference in New Issue
Block a user