mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-07 22:24:06 -05:00
fix(slack): respect message limit, remove duplicate canonical representations (#2469)
* fix(slack): respect message limit, remove duplicate canonical representations * removed comment * updated docs script --------- Co-authored-by: aadamgough <adam@sim.ai>
This commit is contained in:
@@ -120,117 +120,117 @@ import {
|
||||
type IconComponent = ComponentType<SVGProps<SVGSVGElement>>
|
||||
|
||||
export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
zoom: ZoomIcon,
|
||||
zep: ZepIcon,
|
||||
zendesk: ZendeskIcon,
|
||||
youtube: YouTubeIcon,
|
||||
x: xIcon,
|
||||
wordpress: WordpressIcon,
|
||||
wikipedia: WikipediaIcon,
|
||||
whatsapp: WhatsAppIcon,
|
||||
webflow: WebflowIcon,
|
||||
wealthbox: WealthboxIcon,
|
||||
vision: EyeIcon,
|
||||
video_generator: VideoIcon,
|
||||
typeform: TypeformIcon,
|
||||
twilio_voice: TwilioIcon,
|
||||
twilio_sms: TwilioIcon,
|
||||
tts: TTSIcon,
|
||||
trello: TrelloIcon,
|
||||
translate: TranslateIcon,
|
||||
thinking: BrainIcon,
|
||||
telegram: TelegramIcon,
|
||||
tavily: TavilyIcon,
|
||||
supabase: SupabaseIcon,
|
||||
stt: STTIcon,
|
||||
stripe: StripeIcon,
|
||||
stagehand: StagehandIcon,
|
||||
ssh: SshIcon,
|
||||
sqs: SQSIcon,
|
||||
spotify: SpotifyIcon,
|
||||
smtp: SmtpIcon,
|
||||
slack: SlackIcon,
|
||||
shopify: ShopifyIcon,
|
||||
sharepoint: MicrosoftSharepointIcon,
|
||||
sftp: SftpIcon,
|
||||
servicenow: ServiceNowIcon,
|
||||
serper: SerperIcon,
|
||||
sentry: SentryIcon,
|
||||
sendgrid: SendgridIcon,
|
||||
search: SearchIcon,
|
||||
salesforce: SalesforceIcon,
|
||||
s3: S3Icon,
|
||||
resend: ResendIcon,
|
||||
reddit: RedditIcon,
|
||||
rds: RDSIcon,
|
||||
qdrant: QdrantIcon,
|
||||
posthog: PosthogIcon,
|
||||
postgresql: PostgresIcon,
|
||||
polymarket: PolymarketIcon,
|
||||
pipedrive: PipedriveIcon,
|
||||
pinecone: PineconeIcon,
|
||||
perplexity: PerplexityIcon,
|
||||
parallel_ai: ParallelIcon,
|
||||
outlook: OutlookIcon,
|
||||
openai: OpenAIIcon,
|
||||
onedrive: MicrosoftOneDriveIcon,
|
||||
notion: NotionIcon,
|
||||
neo4j: Neo4jIcon,
|
||||
mysql: MySQLIcon,
|
||||
mongodb: MongoDBIcon,
|
||||
mistral_parse: MistralIcon,
|
||||
microsoft_teams: MicrosoftTeamsIcon,
|
||||
microsoft_planner: MicrosoftPlannerIcon,
|
||||
microsoft_excel: MicrosoftExcelIcon,
|
||||
memory: BrainIcon,
|
||||
mem0: Mem0Icon,
|
||||
mailgun: MailgunIcon,
|
||||
mailchimp: MailchimpIcon,
|
||||
linkup: LinkupIcon,
|
||||
linkedin: LinkedInIcon,
|
||||
linear: LinearIcon,
|
||||
knowledge: PackageSearchIcon,
|
||||
kalshi: KalshiIcon,
|
||||
jira: JiraIcon,
|
||||
jina: JinaAIIcon,
|
||||
intercom: IntercomIcon,
|
||||
incidentio: IncidentioIcon,
|
||||
image_generator: ImageIcon,
|
||||
hunter: HunterIOIcon,
|
||||
huggingface: HuggingFaceIcon,
|
||||
hubspot: HubspotIcon,
|
||||
grafana: GrafanaIcon,
|
||||
google_vault: GoogleVaultIcon,
|
||||
google_slides: GoogleSlidesIcon,
|
||||
google_sheets: GoogleSheetsIcon,
|
||||
google_groups: GoogleGroupsIcon,
|
||||
google_forms: GoogleFormsIcon,
|
||||
google_drive: GoogleDriveIcon,
|
||||
google_docs: GoogleDocsIcon,
|
||||
google_calendar: GoogleCalendarIcon,
|
||||
google_search: GoogleIcon,
|
||||
gmail: GmailIcon,
|
||||
gitlab: GitLabIcon,
|
||||
github: GithubIcon,
|
||||
firecrawl: FirecrawlIcon,
|
||||
file: DocumentIcon,
|
||||
exa: ExaAIIcon,
|
||||
elevenlabs: ElevenLabsIcon,
|
||||
elasticsearch: ElasticsearchIcon,
|
||||
dynamodb: DynamoDBIcon,
|
||||
duckduckgo: DuckDuckGoIcon,
|
||||
dropbox: DropboxIcon,
|
||||
discord: DiscordIcon,
|
||||
datadog: DatadogIcon,
|
||||
cursor: CursorIcon,
|
||||
confluence: ConfluenceIcon,
|
||||
clay: ClayIcon,
|
||||
calendly: CalendlyIcon,
|
||||
browser_use: BrowserUseIcon,
|
||||
asana: AsanaIcon,
|
||||
arxiv: ArxivIcon,
|
||||
apollo: ApolloIcon,
|
||||
apify: ApifyIcon,
|
||||
airtable: AirtableIcon,
|
||||
ahrefs: AhrefsIcon,
|
||||
airtable: AirtableIcon,
|
||||
apify: ApifyIcon,
|
||||
apollo: ApolloIcon,
|
||||
arxiv: ArxivIcon,
|
||||
asana: AsanaIcon,
|
||||
browser_use: BrowserUseIcon,
|
||||
calendly: CalendlyIcon,
|
||||
clay: ClayIcon,
|
||||
confluence: ConfluenceIcon,
|
||||
cursor: CursorIcon,
|
||||
datadog: DatadogIcon,
|
||||
discord: DiscordIcon,
|
||||
dropbox: DropboxIcon,
|
||||
duckduckgo: DuckDuckGoIcon,
|
||||
dynamodb: DynamoDBIcon,
|
||||
elasticsearch: ElasticsearchIcon,
|
||||
elevenlabs: ElevenLabsIcon,
|
||||
exa: ExaAIIcon,
|
||||
file: DocumentIcon,
|
||||
firecrawl: FirecrawlIcon,
|
||||
github: GithubIcon,
|
||||
gitlab: GitLabIcon,
|
||||
gmail: GmailIcon,
|
||||
google_calendar: GoogleCalendarIcon,
|
||||
google_docs: GoogleDocsIcon,
|
||||
google_drive: GoogleDriveIcon,
|
||||
google_forms: GoogleFormsIcon,
|
||||
google_groups: GoogleGroupsIcon,
|
||||
google_search: GoogleIcon,
|
||||
google_sheets: GoogleSheetsIcon,
|
||||
google_slides: GoogleSlidesIcon,
|
||||
google_vault: GoogleVaultIcon,
|
||||
grafana: GrafanaIcon,
|
||||
hubspot: HubspotIcon,
|
||||
huggingface: HuggingFaceIcon,
|
||||
hunter: HunterIOIcon,
|
||||
image_generator: ImageIcon,
|
||||
incidentio: IncidentioIcon,
|
||||
intercom: IntercomIcon,
|
||||
jina: JinaAIIcon,
|
||||
jira: JiraIcon,
|
||||
kalshi: KalshiIcon,
|
||||
knowledge: PackageSearchIcon,
|
||||
linear: LinearIcon,
|
||||
linkedin: LinkedInIcon,
|
||||
linkup: LinkupIcon,
|
||||
mailchimp: MailchimpIcon,
|
||||
mailgun: MailgunIcon,
|
||||
mem0: Mem0Icon,
|
||||
memory: BrainIcon,
|
||||
microsoft_excel: MicrosoftExcelIcon,
|
||||
microsoft_planner: MicrosoftPlannerIcon,
|
||||
microsoft_teams: MicrosoftTeamsIcon,
|
||||
mistral_parse: MistralIcon,
|
||||
mongodb: MongoDBIcon,
|
||||
mysql: MySQLIcon,
|
||||
neo4j: Neo4jIcon,
|
||||
notion: NotionIcon,
|
||||
onedrive: MicrosoftOneDriveIcon,
|
||||
openai: OpenAIIcon,
|
||||
outlook: OutlookIcon,
|
||||
parallel_ai: ParallelIcon,
|
||||
perplexity: PerplexityIcon,
|
||||
pinecone: PineconeIcon,
|
||||
pipedrive: PipedriveIcon,
|
||||
polymarket: PolymarketIcon,
|
||||
postgresql: PostgresIcon,
|
||||
posthog: PosthogIcon,
|
||||
qdrant: QdrantIcon,
|
||||
rds: RDSIcon,
|
||||
reddit: RedditIcon,
|
||||
resend: ResendIcon,
|
||||
s3: S3Icon,
|
||||
salesforce: SalesforceIcon,
|
||||
search: SearchIcon,
|
||||
sendgrid: SendgridIcon,
|
||||
sentry: SentryIcon,
|
||||
serper: SerperIcon,
|
||||
servicenow: ServiceNowIcon,
|
||||
sftp: SftpIcon,
|
||||
sharepoint: MicrosoftSharepointIcon,
|
||||
shopify: ShopifyIcon,
|
||||
slack: SlackIcon,
|
||||
smtp: SmtpIcon,
|
||||
spotify: SpotifyIcon,
|
||||
sqs: SQSIcon,
|
||||
ssh: SshIcon,
|
||||
stagehand: StagehandIcon,
|
||||
stripe: StripeIcon,
|
||||
stt: STTIcon,
|
||||
supabase: SupabaseIcon,
|
||||
tavily: TavilyIcon,
|
||||
telegram: TelegramIcon,
|
||||
thinking: BrainIcon,
|
||||
translate: TranslateIcon,
|
||||
trello: TrelloIcon,
|
||||
tts: TTSIcon,
|
||||
twilio_sms: TwilioIcon,
|
||||
twilio_voice: TwilioIcon,
|
||||
typeform: TypeformIcon,
|
||||
video_generator: VideoIcon,
|
||||
vision: EyeIcon,
|
||||
wealthbox: WealthboxIcon,
|
||||
webflow: WebflowIcon,
|
||||
whatsapp: WhatsAppIcon,
|
||||
wikipedia: WikipediaIcon,
|
||||
wordpress: WordpressIcon,
|
||||
x: xIcon,
|
||||
youtube: YouTubeIcon,
|
||||
zendesk: ZendeskIcon,
|
||||
zep: ZepIcon,
|
||||
zoom: ZoomIcon,
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ Read the latest messages from Slack channels. Retrieve conversation history with
|
||||
| `botToken` | string | No | Bot token for Custom Bot |
|
||||
| `channel` | string | No | Slack channel to read messages from \(e.g., #general\) |
|
||||
| `userId` | string | No | User ID for DM conversation \(e.g., U1234567890\) |
|
||||
| `limit` | number | No | Number of messages to retrieve \(default: 10, max: 100\) |
|
||||
| `limit` | number | No | Number of messages to retrieve \(default: 10, max: 15\) |
|
||||
| `oldest` | string | No | Start of time range \(timestamp\) |
|
||||
| `latest` | string | No | End of time range \(timestamp\) |
|
||||
|
||||
|
||||
@@ -14,7 +14,12 @@ const SlackReadMessagesSchema = z
|
||||
accessToken: z.string().min(1, 'Access token is required'),
|
||||
channel: z.string().optional().nullable(),
|
||||
userId: z.string().optional().nullable(),
|
||||
limit: z.number().optional().nullable(),
|
||||
limit: z.coerce
|
||||
.number()
|
||||
.min(1, 'Limit must be at least 1')
|
||||
.max(15, 'Limit cannot exceed 15')
|
||||
.optional()
|
||||
.nullable(),
|
||||
oldest: z.string().optional().nullable(),
|
||||
latest: z.string().optional().nullable(),
|
||||
})
|
||||
@@ -62,8 +67,8 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
const url = new URL('https://slack.com/api/conversations.history')
|
||||
url.searchParams.append('channel', channel!)
|
||||
const limit = validatedData.limit ? Number(validatedData.limit) : 10
|
||||
url.searchParams.append('limit', String(Math.min(limit, 15)))
|
||||
const limit = validatedData.limit ?? 10
|
||||
url.searchParams.append('limit', String(limit))
|
||||
|
||||
if (validatedData.oldest) {
|
||||
url.searchParams.append('oldest', validatedData.oldest)
|
||||
|
||||
@@ -134,7 +134,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
placeholder: 'Enter the bucket ID',
|
||||
condition: { field: 'operation', value: ['read_bucket', 'update_bucket', 'delete_bucket'] },
|
||||
dependsOn: ['credential'],
|
||||
canonicalParamId: 'bucketId',
|
||||
},
|
||||
|
||||
// ETag for update/delete operations
|
||||
|
||||
@@ -181,7 +181,6 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
|
||||
id: 'threadTs',
|
||||
title: 'Thread Timestamp',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'thread_ts',
|
||||
placeholder: 'Reply to thread (e.g., 1405894322.002768)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -263,7 +262,6 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
|
||||
id: 'channelLimit',
|
||||
title: 'Channel Limit',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'limit',
|
||||
placeholder: '100',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -275,7 +273,6 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
|
||||
id: 'memberLimit',
|
||||
title: 'Member Limit',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'limit',
|
||||
placeholder: '100',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -301,7 +298,6 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
|
||||
id: 'userLimit',
|
||||
title: 'User Limit',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'limit',
|
||||
placeholder: '100',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -358,7 +354,6 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
|
||||
id: 'updateTimestamp',
|
||||
title: 'Message Timestamp',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'timestamp',
|
||||
placeholder: 'Message timestamp (e.g., 1405894322.002768)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -382,7 +377,6 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
|
||||
id: 'deleteTimestamp',
|
||||
title: 'Message Timestamp',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'timestamp',
|
||||
placeholder: 'Message timestamp (e.g., 1405894322.002768)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -395,7 +389,6 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
|
||||
id: 'reactionTimestamp',
|
||||
title: 'Message Timestamp',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'timestamp',
|
||||
placeholder: 'Message timestamp (e.g., 1405894322.002768)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -407,7 +400,6 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
|
||||
id: 'emojiName',
|
||||
title: 'Emoji Name',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'name',
|
||||
placeholder: 'Emoji name without colons (e.g., thumbsup, heart, eyes)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -554,47 +546,35 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
|
||||
baseParams.content = content
|
||||
break
|
||||
|
||||
case 'read':
|
||||
if (limit) {
|
||||
const parsedLimit = Number.parseInt(limit, 10)
|
||||
baseParams.limit = !Number.isNaN(parsedLimit) ? parsedLimit : 10
|
||||
} else {
|
||||
baseParams.limit = 10
|
||||
case 'read': {
|
||||
const parsedLimit = limit ? Number.parseInt(limit, 10) : 10
|
||||
if (Number.isNaN(parsedLimit) || parsedLimit < 1 || parsedLimit > 15) {
|
||||
throw new Error('Message limit must be between 1 and 15')
|
||||
}
|
||||
baseParams.limit = parsedLimit
|
||||
if (oldest) {
|
||||
baseParams.oldest = oldest
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'list_channels':
|
||||
case 'list_channels': {
|
||||
baseParams.includePrivate = includePrivate !== 'false'
|
||||
baseParams.excludeArchived = true
|
||||
if (channelLimit) {
|
||||
const parsedLimit = Number.parseInt(channelLimit, 10)
|
||||
baseParams.limit = !Number.isNaN(parsedLimit) ? parsedLimit : 100
|
||||
} else {
|
||||
baseParams.limit = 100
|
||||
}
|
||||
baseParams.limit = channelLimit ? Number.parseInt(channelLimit, 10) : 100
|
||||
break
|
||||
}
|
||||
|
||||
case 'list_members':
|
||||
if (memberLimit) {
|
||||
const parsedLimit = Number.parseInt(memberLimit, 10)
|
||||
baseParams.limit = !Number.isNaN(parsedLimit) ? parsedLimit : 100
|
||||
} else {
|
||||
baseParams.limit = 100
|
||||
}
|
||||
case 'list_members': {
|
||||
baseParams.limit = memberLimit ? Number.parseInt(memberLimit, 10) : 100
|
||||
break
|
||||
}
|
||||
|
||||
case 'list_users':
|
||||
case 'list_users': {
|
||||
baseParams.includeDeleted = includeDeleted === 'true'
|
||||
if (userLimit) {
|
||||
const parsedLimit = Number.parseInt(userLimit, 10)
|
||||
baseParams.limit = !Number.isNaN(parsedLimit) ? parsedLimit : 100
|
||||
} else {
|
||||
baseParams.limit = 100
|
||||
}
|
||||
baseParams.limit = userLimit ? Number.parseInt(userLimit, 10) : 100
|
||||
break
|
||||
}
|
||||
|
||||
case 'get_user':
|
||||
if (!userId) {
|
||||
|
||||
@@ -70,17 +70,6 @@ export const WealthboxBlock: BlockConfig<WealthboxResponse> = {
|
||||
title: 'Task ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter Task ID',
|
||||
mode: 'basic',
|
||||
canonicalParamId: 'taskId',
|
||||
condition: { field: 'operation', value: ['read_task'] },
|
||||
},
|
||||
{
|
||||
id: 'manualTaskId',
|
||||
title: 'Task ID',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'taskId',
|
||||
placeholder: 'Enter Task ID',
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: ['read_task'] },
|
||||
},
|
||||
{
|
||||
@@ -167,12 +156,9 @@ export const WealthboxBlock: BlockConfig<WealthboxResponse> = {
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { credential, operation, contactId, manualContactId, taskId, manualTaskId, ...rest } =
|
||||
params
|
||||
const { credential, operation, contactId, manualContactId, taskId, ...rest } = params
|
||||
|
||||
// Handle both selector and manual inputs
|
||||
const effectiveContactId = (contactId || manualContactId || '').trim()
|
||||
const effectiveTaskId = (taskId || manualTaskId || '').trim()
|
||||
|
||||
const baseParams = {
|
||||
...rest,
|
||||
@@ -225,7 +211,6 @@ export const WealthboxBlock: BlockConfig<WealthboxResponse> = {
|
||||
contactId: { type: 'string', description: 'Contact identifier' },
|
||||
manualContactId: { type: 'string', description: 'Manual contact identifier' },
|
||||
taskId: { type: 'string', description: 'Task identifier' },
|
||||
manualTaskId: { type: 'string', description: 'Manual task identifier' },
|
||||
content: { type: 'string', description: 'Content text' },
|
||||
firstName: { type: 'string', description: 'First name' },
|
||||
lastName: { type: 'string', description: 'Last name' },
|
||||
|
||||
@@ -51,7 +51,7 @@ export const slackMessageReaderTool: ToolConfig<
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of messages to retrieve (default: 10, max: 100)',
|
||||
description: 'Number of messages to retrieve (default: 10, max: 15)',
|
||||
},
|
||||
oldest: {
|
||||
type: 'string',
|
||||
|
||||
@@ -71,7 +71,7 @@ async function generateIconMapping(): Promise<Record<string, string>> {
|
||||
console.log('Generating icon mapping from block definitions...')
|
||||
|
||||
const iconMapping: Record<string, string> = {}
|
||||
const blockFiles = await glob(`${BLOCKS_PATH}/*.ts`)
|
||||
const blockFiles = (await glob(`${BLOCKS_PATH}/*.ts`)).sort()
|
||||
|
||||
for (const blockFile of blockFiles) {
|
||||
const fileContent = fs.readFileSync(blockFile, 'utf-8')
|
||||
@@ -132,6 +132,7 @@ function writeIconMapping(iconMapping: Record<string, string>): void {
|
||||
|
||||
// Generate mapping with direct references (no dynamic access for tree shaking)
|
||||
const mappingEntries = Object.entries(iconMapping)
|
||||
.sort(([a], [b]) => a.localeCompare(b))
|
||||
.map(([blockType, iconName]) => ` ${blockType}: ${iconName},`)
|
||||
.join('\n')
|
||||
|
||||
@@ -1165,7 +1166,7 @@ async function generateAllBlockDocs() {
|
||||
const iconMapping = await generateIconMapping()
|
||||
writeIconMapping(iconMapping)
|
||||
|
||||
const blockFiles = await glob(`${BLOCKS_PATH}/*.ts`)
|
||||
const blockFiles = (await glob(`${BLOCKS_PATH}/*.ts`)).sort()
|
||||
|
||||
for (const blockFile of blockFiles) {
|
||||
await generateBlockDoc(blockFile)
|
||||
|
||||
Reference in New Issue
Block a user