Skip to content

Commit 127994f

Browse files
authored
feat(slack): add remove reaction tool (#3414)
* feat(slack): add remove reaction tool * lint
1 parent efc1aee commit 127994f

File tree

7 files changed

+252
-6
lines changed

7 files changed

+252
-6
lines changed

apps/docs/content/docs/en/tools/slack.mdx

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Slack
3-
description: Send, update, delete messages, send ephemeral messages, add reactions in Slack or trigger workflows from Slack events
3+
description: Send, update, delete messages, send ephemeral messages, add or remove reactions in Slack or trigger workflows from Slack events
44
---
55

66
import { BlockInfoCard } from "@/components/ui/block-info-card"
@@ -39,7 +39,7 @@ If you encounter issues with the Slack integration, contact us at [help@sim.ai](
3939

4040
## Usage Instructions
4141

42-
Integrate Slack into the workflow. Can send, update, and delete messages, send ephemeral messages visible only to a specific user, create canvases, read messages, and add reactions. Requires Bot Token instead of OAuth in advanced mode. Can be used in trigger mode to trigger a workflow when a message is sent to a channel.
42+
Integrate Slack into the workflow. Can send, update, and delete messages, send ephemeral messages visible only to a specific user, create canvases, read messages, and add or remove reactions. Requires Bot Token instead of OAuth in advanced mode. Can be used in trigger mode to trigger a workflow when a message is sent to a channel.
4343

4444

4545

@@ -799,4 +799,28 @@ Add an emoji reaction to a Slack message
799799
|`timestamp` | string | Message timestamp |
800800
|`reaction` | string | Emoji reaction name |
801801

802+
### `slack_remove_reaction`
803+
804+
Remove an emoji reaction from a Slack message
805+
806+
#### Input
807+
808+
| Parameter | Type | Required | Description |
809+
| --------- | ---- | -------- | ----------- |
810+
| `authMethod` | string | No | Authentication method: oauth or bot_token |
811+
| `botToken` | string | No | Bot token for Custom Bot |
812+
| `channel` | string | Yes | Channel ID where the message was posted \(e.g., C1234567890\) |
813+
| `timestamp` | string | Yes | Timestamp of the message to remove reaction from \(e.g., 1405894322.002768\) |
814+
| `name` | string | Yes | Name of the emoji reaction to remove \(without colons, e.g., thumbsup, heart, eyes\) |
815+
816+
#### Output
817+
818+
| Parameter | Type | Description |
819+
| --------- | ---- | ----------- |
820+
| `content` | string | Success message |
821+
| `metadata` | object | Reaction metadata |
822+
|`channel` | string | Channel ID |
823+
|`timestamp` | string | Message timestamp |
824+
|`reaction` | string | Emoji reaction name |
825+
802826

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { type NextRequest, NextResponse } from 'next/server'
2+
import { z } from 'zod'
3+
import { checkInternalAuth } from '@/lib/auth/hybrid'
4+
5+
export const dynamic = 'force-dynamic'
6+
7+
const SlackRemoveReactionSchema = z.object({
8+
accessToken: z.string().min(1, 'Access token is required'),
9+
channel: z.string().min(1, 'Channel is required'),
10+
timestamp: z.string().min(1, 'Message timestamp is required'),
11+
name: z.string().min(1, 'Emoji name is required'),
12+
})
13+
14+
export async function POST(request: NextRequest) {
15+
try {
16+
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
17+
18+
if (!authResult.success) {
19+
return NextResponse.json(
20+
{
21+
success: false,
22+
error: authResult.error || 'Authentication required',
23+
},
24+
{ status: 401 }
25+
)
26+
}
27+
28+
const body = await request.json()
29+
const validatedData = SlackRemoveReactionSchema.parse(body)
30+
31+
const slackResponse = await fetch('https://slack.com/api/reactions.remove', {
32+
method: 'POST',
33+
headers: {
34+
'Content-Type': 'application/json',
35+
Authorization: `Bearer ${validatedData.accessToken}`,
36+
},
37+
body: JSON.stringify({
38+
channel: validatedData.channel,
39+
timestamp: validatedData.timestamp,
40+
name: validatedData.name,
41+
}),
42+
})
43+
44+
const data = await slackResponse.json()
45+
46+
if (!data.ok) {
47+
return NextResponse.json(
48+
{
49+
success: false,
50+
error: data.error || 'Failed to remove reaction',
51+
},
52+
{ status: slackResponse.status }
53+
)
54+
}
55+
56+
return NextResponse.json({
57+
success: true,
58+
output: {
59+
content: `Successfully removed :${validatedData.name}: reaction`,
60+
metadata: {
61+
channel: validatedData.channel,
62+
timestamp: validatedData.timestamp,
63+
reaction: validatedData.name,
64+
},
65+
},
66+
})
67+
} catch (error) {
68+
if (error instanceof z.ZodError) {
69+
return NextResponse.json(
70+
{
71+
success: false,
72+
error: 'Invalid request data',
73+
details: error.errors,
74+
},
75+
{ status: 400 }
76+
)
77+
}
78+
79+
return NextResponse.json(
80+
{
81+
success: false,
82+
error: error instanceof Error ? error.message : 'Unknown error occurred',
83+
},
84+
{ status: 500 }
85+
)
86+
}
87+
}

apps/sim/blocks/blocks/slack.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
99
type: 'slack',
1010
name: 'Slack',
1111
description:
12-
'Send, update, delete messages, send ephemeral messages, add reactions in Slack or trigger workflows from Slack events',
12+
'Send, update, delete messages, send ephemeral messages, add or remove reactions in Slack or trigger workflows from Slack events',
1313
authMode: AuthMode.OAuth,
1414
longDescription:
15-
'Integrate Slack into the workflow. Can send, update, and delete messages, send ephemeral messages visible only to a specific user, create canvases, read messages, and add reactions. Requires Bot Token instead of OAuth in advanced mode. Can be used in trigger mode to trigger a workflow when a message is sent to a channel.',
15+
'Integrate Slack into the workflow. Can send, update, and delete messages, send ephemeral messages visible only to a specific user, create canvases, read messages, and add or remove reactions. Requires Bot Token instead of OAuth in advanced mode. Can be used in trigger mode to trigger a workflow when a message is sent to a channel.',
1616
docsLink: 'https://docs.sim.ai/tools/slack',
1717
category: 'tools',
1818
bgColor: '#611f69',
@@ -38,6 +38,7 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
3838
{ label: 'Update Message', id: 'update' },
3939
{ label: 'Delete Message', id: 'delete' },
4040
{ label: 'Add Reaction', id: 'react' },
41+
{ label: 'Remove Reaction', id: 'unreact' },
4142
],
4243
value: () => 'send',
4344
},
@@ -608,7 +609,7 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
608609
placeholder: 'Message timestamp (e.g., 1405894322.002768)',
609610
condition: {
610611
field: 'operation',
611-
value: 'react',
612+
value: ['react', 'unreact'],
612613
},
613614
required: true,
614615
},
@@ -619,7 +620,7 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
619620
placeholder: 'Emoji name without colons (e.g., thumbsup, heart, eyes)',
620621
condition: {
621622
field: 'operation',
622-
value: 'react',
623+
value: ['react', 'unreact'],
623624
},
624625
required: true,
625626
},
@@ -641,6 +642,7 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
641642
'slack_update_message',
642643
'slack_delete_message',
643644
'slack_add_reaction',
645+
'slack_remove_reaction',
644646
],
645647
config: {
646648
tool: (params) => {
@@ -673,6 +675,8 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
673675
return 'slack_delete_message'
674676
case 'react':
675677
return 'slack_add_reaction'
678+
case 'unreact':
679+
return 'slack_remove_reaction'
676680
default:
677681
throw new Error(`Invalid Slack operation: ${params.operation}`)
678682
}
@@ -841,6 +845,7 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
841845
break
842846

843847
case 'react':
848+
case 'unreact':
844849
baseParams.timestamp = reactionTimestamp
845850
baseParams.name = emojiName
846851
break

apps/sim/tools/registry.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1808,6 +1808,7 @@ import {
18081808
slackListUsersTool,
18091809
slackMessageReaderTool,
18101810
slackMessageTool,
1811+
slackRemoveReactionTool,
18111812
slackUpdateMessageTool,
18121813
} from '@/tools/slack'
18131814
import { smsSendTool } from '@/tools/sms'
@@ -2611,6 +2612,7 @@ export const tools: Record<string, ToolConfig> = {
26112612
slack_update_message: slackUpdateMessageTool,
26122613
slack_delete_message: slackDeleteMessageTool,
26132614
slack_add_reaction: slackAddReactionTool,
2615+
slack_remove_reaction: slackRemoveReactionTool,
26142616
github_repo_info: githubRepoInfoTool,
26152617
github_repo_info_v2: githubRepoInfoV2Tool,
26162618
github_latest_commit: githubLatestCommitTool,

apps/sim/tools/slack/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { slackListMembersTool } from '@/tools/slack/list_members'
1111
import { slackListUsersTool } from '@/tools/slack/list_users'
1212
import { slackMessageTool } from '@/tools/slack/message'
1313
import { slackMessageReaderTool } from '@/tools/slack/message_reader'
14+
import { slackRemoveReactionTool } from '@/tools/slack/remove_reaction'
1415
import { slackUpdateMessageTool } from '@/tools/slack/update_message'
1516

1617
export {
@@ -22,6 +23,7 @@ export {
2223
slackUpdateMessageTool,
2324
slackDeleteMessageTool,
2425
slackAddReactionTool,
26+
slackRemoveReactionTool,
2527
slackListChannelsTool,
2628
slackListMembersTool,
2729
slackListUsersTool,
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import type { SlackRemoveReactionParams, SlackRemoveReactionResponse } from '@/tools/slack/types'
2+
import { REACTION_METADATA_OUTPUT_PROPERTIES } from '@/tools/slack/types'
3+
import type { ToolConfig } from '@/tools/types'
4+
5+
export const slackRemoveReactionTool: ToolConfig<
6+
SlackRemoveReactionParams,
7+
SlackRemoveReactionResponse
8+
> = {
9+
id: 'slack_remove_reaction',
10+
name: 'Slack Remove Reaction',
11+
description: 'Remove an emoji reaction from a Slack message',
12+
version: '1.0.0',
13+
14+
oauth: {
15+
required: true,
16+
provider: 'slack',
17+
},
18+
19+
params: {
20+
authMethod: {
21+
type: 'string',
22+
required: false,
23+
visibility: 'user-only',
24+
description: 'Authentication method: oauth or bot_token',
25+
},
26+
botToken: {
27+
type: 'string',
28+
required: false,
29+
visibility: 'user-only',
30+
description: 'Bot token for Custom Bot',
31+
},
32+
accessToken: {
33+
type: 'string',
34+
required: false,
35+
visibility: 'hidden',
36+
description: 'OAuth access token or bot token for Slack API',
37+
},
38+
channel: {
39+
type: 'string',
40+
required: true,
41+
visibility: 'user-or-llm',
42+
description: 'Channel ID where the message was posted (e.g., C1234567890)',
43+
},
44+
timestamp: {
45+
type: 'string',
46+
required: true,
47+
visibility: 'user-or-llm',
48+
description: 'Timestamp of the message to remove reaction from (e.g., 1405894322.002768)',
49+
},
50+
name: {
51+
type: 'string',
52+
required: true,
53+
visibility: 'user-or-llm',
54+
description:
55+
'Name of the emoji reaction to remove (without colons, e.g., thumbsup, heart, eyes)',
56+
},
57+
},
58+
59+
request: {
60+
url: '/api/tools/slack/remove-reaction',
61+
method: 'POST',
62+
headers: () => ({
63+
'Content-Type': 'application/json',
64+
}),
65+
body: (params: SlackRemoveReactionParams) => ({
66+
accessToken: params.accessToken || params.botToken,
67+
channel: params.channel,
68+
timestamp: params.timestamp,
69+
name: params.name,
70+
}),
71+
},
72+
73+
transformResponse: async (response: Response) => {
74+
const data = await response.json()
75+
76+
if (!data.success) {
77+
return {
78+
success: false,
79+
output: {
80+
content: data.error || 'Failed to remove reaction',
81+
metadata: {
82+
channel: '',
83+
timestamp: '',
84+
reaction: '',
85+
},
86+
},
87+
error: data.error,
88+
}
89+
}
90+
91+
return {
92+
success: true,
93+
output: {
94+
content: data.output.content,
95+
metadata: data.output.metadata,
96+
},
97+
}
98+
},
99+
100+
outputs: {
101+
content: { type: 'string', description: 'Success message' },
102+
metadata: {
103+
type: 'object',
104+
description: 'Reaction metadata',
105+
properties: REACTION_METADATA_OUTPUT_PROPERTIES,
106+
},
107+
},
108+
}

apps/sim/tools/slack/types.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,12 @@ export interface SlackAddReactionParams extends SlackBaseParams {
561561
name: string
562562
}
563563

564+
export interface SlackRemoveReactionParams extends SlackBaseParams {
565+
channel: string
566+
timestamp: string
567+
name: string
568+
}
569+
564570
export interface SlackListChannelsParams extends SlackBaseParams {
565571
includePrivate?: boolean
566572
excludeArchived?: boolean
@@ -759,6 +765,17 @@ export interface SlackAddReactionResponse extends ToolResponse {
759765
}
760766
}
761767

768+
export interface SlackRemoveReactionResponse extends ToolResponse {
769+
output: {
770+
content: string
771+
metadata: {
772+
channel: string
773+
timestamp: string
774+
reaction: string
775+
}
776+
}
777+
}
778+
762779
export interface SlackChannel {
763780
id: string
764781
name: string
@@ -866,6 +883,7 @@ export type SlackResponse =
866883
| SlackUpdateMessageResponse
867884
| SlackDeleteMessageResponse
868885
| SlackAddReactionResponse
886+
| SlackRemoveReactionResponse
869887
| SlackListChannelsResponse
870888
| SlackListMembersResponse
871889
| SlackListUsersResponse

0 commit comments

Comments
 (0)