@bubblelab/bubble-core 0.1.17 → 0.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/bubble-bundle.d.ts +84 -79
  2. package/dist/bubble-factory.js +1 -1
  3. package/dist/bubble-factory.js.map +1 -1
  4. package/dist/bubbles/service-bubble/agi-inc.d.ts +112 -112
  5. package/dist/bubbles/service-bubble/ai-agent.d.ts +8 -8
  6. package/dist/bubbles/service-bubble/airtable.d.ts +152 -152
  7. package/dist/bubbles/service-bubble/apify/apify.d.ts +8 -8
  8. package/dist/bubbles/service-bubble/browserbase/browserbase.d.ts +20 -20
  9. package/dist/bubbles/service-bubble/browserbase/browserbase.schema.d.ts +8 -8
  10. package/dist/bubbles/service-bubble/eleven-labs.d.ts +28 -28
  11. package/dist/bubbles/service-bubble/firecrawl.d.ts +140 -140
  12. package/dist/bubbles/service-bubble/followupboss.d.ts +128 -128
  13. package/dist/bubbles/service-bubble/github.d.ts +104 -104
  14. package/dist/bubbles/service-bubble/gmail.d.ts +456 -456
  15. package/dist/bubbles/service-bubble/google-calendar.d.ts +120 -120
  16. package/dist/bubbles/service-bubble/google-drive.d.ts +92 -92
  17. package/dist/bubbles/service-bubble/google-sheets/google-sheets.d.ts +22 -22
  18. package/dist/bubbles/service-bubble/google-sheets/google-sheets.schema.d.ts +10 -10
  19. package/dist/bubbles/service-bubble/hello-world.d.ts +4 -4
  20. package/dist/bubbles/service-bubble/http.d.ts +16 -16
  21. package/dist/bubbles/service-bubble/insforge-db.d.ts +10 -10
  22. package/dist/bubbles/service-bubble/notion/notion.d.ts +76 -76
  23. package/dist/bubbles/service-bubble/postgresql.d.ts +10 -10
  24. package/dist/bubbles/service-bubble/resend.d.ts +32 -32
  25. package/dist/bubbles/service-bubble/slack/index.d.ts +3 -0
  26. package/dist/bubbles/service-bubble/slack/index.d.ts.map +1 -0
  27. package/dist/bubbles/service-bubble/slack/index.js +6 -0
  28. package/dist/bubbles/service-bubble/slack/index.js.map +1 -0
  29. package/dist/bubbles/service-bubble/slack/slack.d.ts +6812 -0
  30. package/dist/bubbles/service-bubble/slack/slack.d.ts.map +1 -0
  31. package/dist/bubbles/service-bubble/slack/slack.js +1840 -0
  32. package/dist/bubbles/service-bubble/slack/slack.js.map +1 -0
  33. package/dist/bubbles/service-bubble/slack/slack.schema.d.ts +3980 -0
  34. package/dist/bubbles/service-bubble/slack/slack.schema.d.ts.map +1 -0
  35. package/dist/bubbles/service-bubble/slack/slack.schema.js +1061 -0
  36. package/dist/bubbles/service-bubble/slack/slack.schema.js.map +1 -0
  37. package/dist/bubbles/service-bubble/slack/slack.utils.d.ts +141 -0
  38. package/dist/bubbles/service-bubble/slack/slack.utils.d.ts.map +1 -0
  39. package/dist/bubbles/service-bubble/slack/slack.utils.js +410 -0
  40. package/dist/bubbles/service-bubble/slack/slack.utils.js.map +1 -0
  41. package/dist/bubbles/service-bubble/slack.d.ts +1 -6811
  42. package/dist/bubbles/service-bubble/slack.d.ts.map +1 -1
  43. package/dist/bubbles/service-bubble/slack.js +2 -1829
  44. package/dist/bubbles/service-bubble/slack.js.map +1 -1
  45. package/dist/bubbles/service-bubble/storage.d.ts +20 -20
  46. package/dist/bubbles/service-bubble/telegram.d.ts +246 -246
  47. package/dist/bubbles/tool-bubble/amazon-shopping-tool/amazon-shopping-tool.d.ts +16 -16
  48. package/dist/bubbles/tool-bubble/amazon-shopping-tool/amazon-shopping-tool.schema.d.ts +6 -6
  49. package/dist/bubbles/tool-bubble/bubbleflow-validation-tool.d.ts +4 -4
  50. package/dist/bubbles/tool-bubble/chart-js-tool.d.ts +4 -4
  51. package/dist/bubbles/tool-bubble/code-edit-tool.d.ts +4 -4
  52. package/dist/bubbles/tool-bubble/get-bubble-details-tool.d.ts +4 -4
  53. package/dist/bubbles/tool-bubble/get-trigger-detail-tool.d.ts +4 -4
  54. package/dist/bubbles/tool-bubble/google-maps-tool.d.ts +16 -16
  55. package/dist/bubbles/tool-bubble/instagram-tool.d.ts +4 -4
  56. package/dist/bubbles/tool-bubble/linkedin-tool.d.ts +34 -34
  57. package/dist/bubbles/tool-bubble/list-bubbles-tool.d.ts +4 -4
  58. package/dist/bubbles/tool-bubble/reddit-scrape-tool.d.ts +14 -14
  59. package/dist/bubbles/tool-bubble/research-agent-tool.d.ts +4 -4
  60. package/dist/bubbles/tool-bubble/sql-query-tool.d.ts +8 -8
  61. package/dist/bubbles/tool-bubble/tiktok-tool.d.ts +12 -12
  62. package/dist/bubbles/tool-bubble/tool-template.d.ts +4 -4
  63. package/dist/bubbles/tool-bubble/twitter-tool.d.ts +14 -14
  64. package/dist/bubbles/tool-bubble/web-crawl-tool.d.ts +4 -4
  65. package/dist/bubbles/tool-bubble/web-extract-tool.d.ts +4 -4
  66. package/dist/bubbles/tool-bubble/web-scrape-tool.d.ts +4 -4
  67. package/dist/bubbles/tool-bubble/web-search-tool.d.ts +10 -10
  68. package/dist/bubbles/tool-bubble/youtube-tool.d.ts +14 -14
  69. package/dist/bubbles/workflow-bubble/database-analyzer.workflow.d.ts +4 -4
  70. package/dist/bubbles/workflow-bubble/generate-document.workflow.d.ts +16 -16
  71. package/dist/bubbles/workflow-bubble/parse-document.workflow.d.ts +4 -4
  72. package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.d.ts +32 -32
  73. package/dist/bubbles/workflow-bubble/pdf-ocr.workflow.d.ts +8 -8
  74. package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.d.ts +8 -8
  75. package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.d.ts.map +1 -1
  76. package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.js +2 -2
  77. package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.js.map +1 -1
  78. package/dist/bubbles/workflow-bubble/slack-formatter-agent.d.ts +40 -40
  79. package/dist/bubbles/workflow-bubble/slack-notifier.workflow.d.ts +4 -4
  80. package/dist/bubbles/workflow-bubble/slack-notifier.workflow.js +1 -1
  81. package/dist/bubbles/workflow-bubble/slack-notifier.workflow.js.map +1 -1
  82. package/dist/bubbles.json +2 -2
  83. package/dist/index.d.ts +1 -1
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +1 -1
  86. package/dist/index.js.map +1 -1
  87. package/package.json +2 -2
  88. package/dist/bubbles/service-bubble/browserbase/browserbase.integration.flow.d.ts +0 -37
  89. package/dist/bubbles/service-bubble/browserbase/browserbase.integration.flow.d.ts.map +0 -1
  90. package/dist/bubbles/service-bubble/browserbase/browserbase.integration.flow.js +0 -203
  91. package/dist/bubbles/service-bubble/browserbase/browserbase.integration.flow.js.map +0 -1
  92. package/dist/bubbles/service-bubble/crustdata/crustdata.d.ts +0 -1358
  93. package/dist/bubbles/service-bubble/crustdata/crustdata.d.ts.map +0 -1
  94. package/dist/bubbles/service-bubble/crustdata/crustdata.js +0 -219
  95. package/dist/bubbles/service-bubble/crustdata/crustdata.js.map +0 -1
  96. package/dist/bubbles/service-bubble/crustdata/crustdata.schema.d.ts +0 -1604
  97. package/dist/bubbles/service-bubble/crustdata/crustdata.schema.d.ts.map +0 -1
  98. package/dist/bubbles/service-bubble/crustdata/crustdata.schema.js +0 -194
  99. package/dist/bubbles/service-bubble/crustdata/crustdata.schema.js.map +0 -1
  100. package/dist/bubbles/service-bubble/crustdata/index.d.ts +0 -3
  101. package/dist/bubbles/service-bubble/crustdata/index.d.ts.map +0 -1
  102. package/dist/bubbles/service-bubble/crustdata/index.js +0 -3
  103. package/dist/bubbles/service-bubble/crustdata/index.js.map +0 -1
  104. package/dist/bubbles/service-bubble/http.integration.flow.d.ts +0 -49
  105. package/dist/bubbles/service-bubble/http.integration.flow.d.ts.map +0 -1
  106. package/dist/bubbles/service-bubble/http.integration.flow.js +0 -425
  107. package/dist/bubbles/service-bubble/http.integration.flow.js.map +0 -1
  108. package/dist/bubbles/tool-bubble/amazon-shopping-tool/amazon-shopping-tool.integration.flow.d.ts +0 -31
  109. package/dist/bubbles/tool-bubble/amazon-shopping-tool/amazon-shopping-tool.integration.flow.d.ts.map +0 -1
  110. package/dist/bubbles/tool-bubble/amazon-shopping-tool/amazon-shopping-tool.integration.flow.js +0 -100
  111. package/dist/bubbles/tool-bubble/amazon-shopping-tool/amazon-shopping-tool.integration.flow.js.map +0 -1
  112. package/dist/bubbles/tool-bubble/company-enrichment-tool.d.ts +0 -740
  113. package/dist/bubbles/tool-bubble/company-enrichment-tool.d.ts.map +0 -1
  114. package/dist/bubbles/tool-bubble/company-enrichment-tool.js +0 -350
  115. package/dist/bubbles/tool-bubble/company-enrichment-tool.js.map +0 -1
@@ -1,1830 +1,3 @@
1
- import { z } from 'zod';
2
- import { ServiceBubble } from '../../types/service-bubble-class.js';
3
- import { CredentialType } from '@bubblelab/shared-schemas';
4
- // Slack API base URL
5
- const SLACK_API_BASE = 'https://slack.com/api';
6
- // Slack operation types are defined inline in the discriminated union below
7
- // Define Slack channel types
8
- const ChannelTypes = z
9
- .enum(['public_channel', 'private_channel', 'mpim', 'im'])
10
- .describe('Types of Slack channels: public_channel, private_channel, mpim (multi-person direct message), im (direct message)');
11
- // Define message attachment schema
12
- const MessageAttachmentSchema = z.object({
13
- color: z
14
- .string()
15
- .optional()
16
- .describe('Color bar accent (hex color or good/warning/danger)'),
17
- pretext: z
18
- .string()
19
- .optional()
20
- .describe('Text that appears before the main attachment content'),
21
- author_name: z
22
- .string()
23
- .optional()
24
- .describe('Author name displayed at the top'),
25
- author_link: z
26
- .string()
27
- .url()
28
- .optional()
29
- .describe('URL to link the author name'),
30
- author_icon: z.string().url().optional().describe('Author icon image URL'),
31
- title: z.string().optional().describe('Attachment title text'),
32
- title_link: z.string().url().optional().describe('URL to link the title'),
33
- text: z.string().optional().describe('Main attachment text content'),
34
- fields: z
35
- .array(z.object({
36
- title: z.string().describe('Field title'),
37
- value: z.string().describe('Field value'),
38
- short: z
39
- .boolean()
40
- .optional()
41
- .describe('Whether field should be displayed side-by-side'),
42
- }))
43
- .optional()
44
- .describe('Array of field objects for structured data'),
45
- image_url: z.string().url().optional().describe('URL of image to display'),
46
- thumb_url: z.string().url().optional().describe('URL of thumbnail image'),
47
- footer: z.string().optional().describe('Footer text'),
48
- footer_icon: z.string().url().optional().describe('Footer icon URL'),
49
- ts: z.number().optional().describe('Timestamp for the attachment'),
50
- });
51
- // Define block kit elements (simplified)
52
- const BlockElementSchema = z
53
- .object({
54
- type: z
55
- .string()
56
- .describe('Block element type (section, divider, button, etc.)'),
57
- text: z
58
- .object({
59
- type: z.enum(['plain_text', 'mrkdwn']).describe('Text formatting type'),
60
- text: z.string().describe('The actual text content'),
61
- emoji: z.boolean().optional(),
62
- verbatim: z.boolean().optional(),
63
- })
64
- .optional()
65
- .describe('Text object for the block element'),
66
- // Add elements field for context blocks
67
- elements: z
68
- .array(z.object({
69
- type: z
70
- .enum(['plain_text', 'mrkdwn', 'image'])
71
- .describe('Element type'),
72
- text: z.string().optional().describe('Text content'),
73
- image_url: z
74
- .string()
75
- .optional()
76
- .describe('Image URL for image elements'),
77
- alt_text: z
78
- .string()
79
- .optional()
80
- .describe('Alt text for image elements'),
81
- emoji: z.boolean().optional(),
82
- verbatim: z.boolean().optional(),
83
- }))
84
- .optional()
85
- .describe('Elements array for context blocks'),
86
- })
87
- .passthrough() // Allow additional properties for different block types
88
- .describe('Block Kit element for rich message formatting');
89
- // Define the parameters schema for different Slack operations
90
- const SlackParamsSchema = z.discriminatedUnion('operation', [
91
- // Send message operation
92
- z.object({
93
- operation: z
94
- .literal('send_message')
95
- .describe('Send a message to a Slack channel or DM. Required scopes: chat:write (add chat:write.public for public channels bot has not joined, add im:write to send DMs to users)'),
96
- channel: z
97
- .string()
98
- .min(1, 'Channel ID or name is required')
99
- .describe('Channel ID (e.g., C1234567890), channel name (e.g., general or #general), or user ID for DM (e.g., U1234567890 - requires im:write scope)'),
100
- text: z
101
- .string()
102
- .min(1, 'Message text is required')
103
- .describe('Message text content'),
104
- username: z
105
- .string()
106
- .optional()
107
- .describe('Override bot username for this message'),
108
- icon_emoji: z
109
- .string()
110
- .optional()
111
- .describe('Override bot icon with emoji (e.g., :robot_face:)'),
112
- icon_url: z
113
- .string()
114
- .url()
115
- .optional()
116
- .describe('Override bot icon with custom image URL'),
117
- attachments: z
118
- .array(MessageAttachmentSchema)
119
- .optional()
120
- .describe('Legacy message attachments'),
121
- blocks: z
122
- .array(BlockElementSchema)
123
- .optional()
124
- .describe('Block Kit structured message blocks'),
125
- thread_ts: z
126
- .string()
127
- .optional()
128
- .describe('Timestamp of parent message to reply in thread'),
129
- reply_broadcast: z
130
- .boolean()
131
- .optional()
132
- .default(false)
133
- .describe('Broadcast thread reply to channel'),
134
- credentials: z
135
- .record(z.nativeEnum(CredentialType), z.string())
136
- .optional()
137
- .describe('Object mapping credential types to values (injected at runtime)'),
138
- unfurl_links: z
139
- .boolean()
140
- .optional()
141
- .default(true)
142
- .describe('Enable automatic link unfurling'),
143
- unfurl_media: z
144
- .boolean()
145
- .optional()
146
- .default(true)
147
- .describe('Enable automatic media unfurling'),
148
- }),
149
- // List channels operation
150
- z.object({
151
- operation: z
152
- .literal('list_channels')
153
- .describe('List all channels in the Slack workspace. Required scopes: channels:read (public), groups:read (private), im:read (DMs), mpim:read (group DMs)'),
154
- types: z
155
- .array(ChannelTypes)
156
- .optional()
157
- .default(['public_channel', 'private_channel'])
158
- .describe('Types of channels to include in results'),
159
- exclude_archived: z
160
- .boolean()
161
- .optional()
162
- .default(true)
163
- .describe('Exclude archived channels from results'),
164
- limit: z
165
- .number()
166
- .min(1)
167
- .max(1000)
168
- .optional()
169
- .default(50)
170
- .describe('Maximum number of channels to return (1-1000)'),
171
- cursor: z
172
- .string()
173
- .optional()
174
- .describe('Cursor for pagination to get next set of results'),
175
- credentials: z
176
- .record(z.nativeEnum(CredentialType), z.string())
177
- .optional()
178
- .describe('Object mapping credential types to values (injected at runtime)'),
179
- }),
180
- // Get channel info operation
181
- z.object({
182
- operation: z
183
- .literal('get_channel_info')
184
- .describe('Get detailed information about a specific channel. Required scopes: channels:read (public), groups:read (private), im:read (DMs), mpim:read (group DMs)'),
185
- channel: z
186
- .string()
187
- .min(1, 'Channel ID or name is required')
188
- .describe('Channel ID (e.g., C1234567890) or channel name (e.g., general or #general)'),
189
- include_locale: z
190
- .boolean()
191
- .optional()
192
- .default(false)
193
- .describe('Include locale information in the response'),
194
- credentials: z
195
- .record(z.nativeEnum(CredentialType), z.string())
196
- .optional()
197
- .describe('Object mapping credential types to values (injected at runtime)'),
198
- }),
199
- // Get user info operation
200
- z.object({
201
- operation: z
202
- .literal('get_user_info')
203
- .describe('Get detailed information about a specific user. Required scopes: users:read (add users:read.email to access email field)'),
204
- user: z
205
- .string()
206
- .min(1, 'User ID is required')
207
- .describe('User ID to get information about'),
208
- include_locale: z
209
- .boolean()
210
- .optional()
211
- .default(false)
212
- .describe('Include locale information in the response'),
213
- credentials: z
214
- .record(z.nativeEnum(CredentialType), z.string())
215
- .optional()
216
- .describe('Object mapping credential types to values (injected at runtime)'),
217
- }),
218
- // List users operation
219
- z.object({
220
- operation: z
221
- .literal('list_users')
222
- .describe('List all users in the Slack workspace. Required scopes: users:read (add users:read.email to access email field)'),
223
- limit: z
224
- .number()
225
- .min(1)
226
- .max(1000)
227
- .optional()
228
- .default(50)
229
- .describe('Maximum number of users to return (1-1000)'),
230
- cursor: z
231
- .string()
232
- .optional()
233
- .describe('Cursor for pagination to get next set of results'),
234
- include_locale: z
235
- .boolean()
236
- .optional()
237
- .default(false)
238
- .describe('Include locale information in the response'),
239
- credentials: z
240
- .record(z.nativeEnum(CredentialType), z.string())
241
- .optional()
242
- .describe('Object mapping credential types to values (injected at runtime)'),
243
- }),
244
- // Get conversation history operation
245
- z.object({
246
- operation: z
247
- .literal('get_conversation_history')
248
- .describe('Retrieve message history from a channel or direct message. Required scopes: channels:history (public), groups:history (private), im:history (DMs), mpim:history (group DMs)'),
249
- channel: z
250
- .string()
251
- .min(1, 'Channel ID or name is required')
252
- .describe('Channel ID (e.g., C1234567890) or channel name (e.g., general or #general)'),
253
- latest: z
254
- .string()
255
- .optional()
256
- .describe('End of time range of messages to include (timestamp)'),
257
- oldest: z
258
- .string()
259
- .optional()
260
- .describe('Start of time range of messages to include (timestamp)'),
261
- inclusive: z
262
- .boolean()
263
- .optional()
264
- .default(false)
265
- .describe('Include messages with latest or oldest timestamps in results'),
266
- limit: z
267
- .number()
268
- .min(1)
269
- .max(1000)
270
- .optional()
271
- .default(20)
272
- .describe('Maximum number of messages to return (1-1000)'),
273
- cursor: z
274
- .string()
275
- .optional()
276
- .describe('Cursor for pagination to get next set of results'),
277
- credentials: z
278
- .record(z.nativeEnum(CredentialType), z.string())
279
- .optional()
280
- .describe('Object mapping credential types to values (injected at runtime)'),
281
- }),
282
- // Get thread replies operation
283
- z.object({
284
- operation: z
285
- .literal('get_thread_replies')
286
- .describe('Retrieve all replies to a thread in a channel. Required scopes: channels:history (public), groups:history (private), im:history (DMs), mpim:history (group DMs)'),
287
- channel: z
288
- .string()
289
- .min(1, 'Channel ID is required')
290
- .describe('Channel ID where the thread exists'),
291
- ts: z
292
- .string()
293
- .min(1, 'Thread timestamp is required')
294
- .describe('Timestamp of the parent message to get replies for'),
295
- latest: z
296
- .string()
297
- .optional()
298
- .describe('End of time range of messages to include (timestamp)'),
299
- oldest: z
300
- .string()
301
- .optional()
302
- .describe('Start of time range of messages to include (timestamp)'),
303
- inclusive: z
304
- .boolean()
305
- .optional()
306
- .default(false)
307
- .describe('Include messages with latest or oldest timestamps in results'),
308
- limit: z
309
- .number()
310
- .min(1)
311
- .max(1000)
312
- .optional()
313
- .default(100)
314
- .describe('Maximum number of messages to return (1-1000)'),
315
- cursor: z
316
- .string()
317
- .optional()
318
- .describe('Cursor for pagination to get next set of results'),
319
- credentials: z
320
- .record(z.nativeEnum(CredentialType), z.string())
321
- .optional()
322
- .describe('Object mapping credential types to values (injected at runtime)'),
323
- }),
324
- // Update message operation
325
- z.object({
326
- operation: z
327
- .literal('update_message')
328
- .describe('Update an existing message in a channel. Required scopes: chat:write'),
329
- channel: z
330
- .string()
331
- .min(1, 'Channel ID or name is required')
332
- .describe('Channel ID (e.g., C1234567890) or channel name (e.g., general or #general) where the message is located'),
333
- ts: z
334
- .string()
335
- .min(1, 'Message timestamp is required')
336
- .describe('Timestamp of the message to update'),
337
- text: z.string().optional().describe('New text content for the message'),
338
- attachments: z
339
- .array(MessageAttachmentSchema)
340
- .optional()
341
- .describe('New legacy message attachments'),
342
- blocks: z
343
- .array(BlockElementSchema)
344
- .optional()
345
- .describe('New Block Kit structured message blocks'),
346
- credentials: z
347
- .record(z.nativeEnum(CredentialType), z.string())
348
- .optional()
349
- .describe('Object mapping credential types to values (injected at runtime)'),
350
- }),
351
- // Delete message operation
352
- z.object({
353
- operation: z
354
- .literal('delete_message')
355
- .describe('Delete a message from a channel. Required scopes: chat:write. Note: Bot tokens can only delete messages posted by the bot; user tokens can delete any message the user has permission to delete'),
356
- channel: z
357
- .string()
358
- .min(1, 'Channel ID or name is required')
359
- .describe('Channel ID (e.g., C1234567890) or channel name (e.g., general or #general) where the message is located'),
360
- ts: z
361
- .string()
362
- .min(1, 'Message timestamp is required')
363
- .describe('Timestamp of the message to delete'),
364
- credentials: z
365
- .record(z.nativeEnum(CredentialType), z.string())
366
- .optional()
367
- .describe('Object mapping credential types to values (injected at runtime)'),
368
- }),
369
- // Add reaction operation
370
- z.object({
371
- operation: z
372
- .literal('add_reaction')
373
- .describe('Add an emoji reaction to a message. Required scopes: reactions:write'),
374
- name: z
375
- .string()
376
- .min(1, 'Emoji name is required')
377
- .describe('Emoji name without colons (e.g., thumbsup, heart)'),
378
- channel: z
379
- .string()
380
- .min(1, 'Channel ID or name is required')
381
- .describe('Channel ID (e.g., C1234567890) or channel name (e.g., general or #general) where the message is located'),
382
- timestamp: z
383
- .string()
384
- .min(1, 'Message timestamp is required')
385
- .describe('Timestamp of the message to react to'),
386
- credentials: z
387
- .record(z.nativeEnum(CredentialType), z.string())
388
- .optional()
389
- .describe('Object mapping credential types to values (injected at runtime)'),
390
- }),
391
- // Remove reaction operation
392
- z.object({
393
- operation: z
394
- .literal('remove_reaction')
395
- .describe('Remove an emoji reaction from a message. Required scopes: reactions:write'),
396
- name: z
397
- .string()
398
- .min(1, 'Emoji name is required')
399
- .describe('Emoji name without colons (e.g., thumbsup, heart)'),
400
- channel: z
401
- .string()
402
- .min(1, 'Channel ID or name is required')
403
- .describe('Channel ID (e.g., C1234567890) or channel name (e.g., general or #general) where the message is located'),
404
- timestamp: z
405
- .string()
406
- .min(1, 'Message timestamp is required')
407
- .describe('Timestamp of the message to remove reaction from'),
408
- credentials: z
409
- .record(z.nativeEnum(CredentialType), z.string())
410
- .optional()
411
- .describe('Object mapping credential types to values (injected at runtime)'),
412
- }),
413
- // Join channel operation
414
- z.object({
415
- operation: z
416
- .literal('join_channel')
417
- .describe('Join a public Slack channel. Required scopes: channels:join (bot token) or channels:write (user token)'),
418
- channel: z
419
- .string()
420
- .min(1, 'Channel ID or name is required')
421
- .describe('Channel ID (e.g., C1234567890) or channel name (e.g., general or #general) to join'),
422
- credentials: z
423
- .record(z.nativeEnum(CredentialType), z.string())
424
- .optional()
425
- .describe('Object mapping credential types to values (injected at runtime)'),
426
- }),
427
- // Upload file operation
428
- z.object({
429
- operation: z
430
- .literal('upload_file')
431
- .describe('Upload a file to a Slack channel. Required scopes: files:write'),
432
- channel: z
433
- .string()
434
- .min(1, 'Channel ID or name is required')
435
- .describe('Channel ID (e.g., C1234567890), channel name (e.g., general or #general), or user ID for DM'),
436
- file_path: z
437
- .string()
438
- .min(1, 'File path is required')
439
- .describe('Local file path to upload'),
440
- filename: z
441
- .string()
442
- .optional()
443
- .describe('Override filename for the upload'),
444
- title: z.string().optional().describe('Title for the file'),
445
- initial_comment: z
446
- .string()
447
- .optional()
448
- .describe('Initial comment to post with the file'),
449
- thread_ts: z
450
- .string()
451
- .optional()
452
- .describe('Timestamp of parent message to upload file in thread'),
453
- credentials: z
454
- .record(z.nativeEnum(CredentialType), z.string())
455
- .optional()
456
- .describe('Object mapping credential types to values (injected at runtime)'),
457
- }),
458
- // Schedule message operation
459
- z.object({
460
- operation: z
461
- .literal('schedule_message')
462
- .describe('Schedule a message to be sent at a future time. Required scopes: chat:write. Max 120 days in advance.'),
463
- channel: z
464
- .string()
465
- .min(1, 'Channel ID or name is required')
466
- .describe('Channel ID (e.g., C1234567890), channel name (e.g., general or #general), or user ID for DM'),
467
- text: z
468
- .string()
469
- .min(1, 'Message text is required')
470
- .describe('Message text content'),
471
- post_at: z
472
- .number()
473
- .int()
474
- .positive()
475
- .describe('Unix timestamp (seconds) for when to send the message. Must be within 120 days from now.'),
476
- thread_ts: z
477
- .string()
478
- .optional()
479
- .describe('Timestamp of parent message to reply in thread'),
480
- blocks: z
481
- .array(BlockElementSchema)
482
- .optional()
483
- .describe('Block Kit structured message blocks'),
484
- unfurl_links: z
485
- .boolean()
486
- .optional()
487
- .default(true)
488
- .describe('Enable automatic link unfurling'),
489
- unfurl_media: z
490
- .boolean()
491
- .optional()
492
- .default(true)
493
- .describe('Enable automatic media unfurling'),
494
- credentials: z
495
- .record(z.nativeEnum(CredentialType), z.string())
496
- .optional()
497
- .describe('Object mapping credential types to values (injected at runtime)'),
498
- }),
499
- ]);
500
- // Define Slack API response schemas for type safety
501
- const SlackChannelSchema = z
502
- .object({
503
- id: z.string().describe('Unique channel identifier'),
504
- name: z.string().describe('Channel name without # prefix'),
505
- is_channel: z
506
- .boolean()
507
- .optional()
508
- .describe('True if this is a public channel'),
509
- is_group: z
510
- .boolean()
511
- .optional()
512
- .describe('True if this is a private channel'),
513
- is_im: z.boolean().optional().describe('True if this is a direct message'),
514
- is_mpim: z
515
- .boolean()
516
- .optional()
517
- .describe('True if this is a multi-person direct message'),
518
- is_private: z
519
- .boolean()
520
- .optional()
521
- .describe('True if this is a private channel'),
522
- created: z.number().describe('Unix timestamp when channel was created'),
523
- is_archived: z.boolean().describe('True if channel is archived'),
524
- is_general: z
525
- .boolean()
526
- .optional()
527
- .describe('True if this is the #general channel'),
528
- unlinked: z
529
- .number()
530
- .optional()
531
- .describe('Unix timestamp when channel was unlinked'),
532
- name_normalized: z.string().optional().describe('Normalized channel name'),
533
- is_shared: z
534
- .boolean()
535
- .optional()
536
- .describe('True if channel is shared with other workspaces'),
537
- is_ext_shared: z
538
- .boolean()
539
- .optional()
540
- .describe('True if channel is shared externally'),
541
- is_org_shared: z
542
- .boolean()
543
- .optional()
544
- .describe('True if channel is shared across organization'),
545
- shared_team_ids: z
546
- .array(z.string())
547
- .optional()
548
- .describe('IDs of teams this channel is shared with'),
549
- pending_shared: z
550
- .array(z.string())
551
- .optional()
552
- .describe('Pending shared connections'),
553
- pending_connected_team_ids: z
554
- .array(z.string())
555
- .optional()
556
- .describe('Pending team connection IDs'),
557
- is_pending_ext_shared: z
558
- .boolean()
559
- .optional()
560
- .describe('True if external sharing is pending'),
561
- is_member: z
562
- .boolean()
563
- .optional()
564
- .describe('True if the bot is a member of this channel'),
565
- is_open: z.boolean().optional().describe('True if the channel is open'),
566
- topic: z
567
- .object({
568
- value: z.string().describe('Topic text'),
569
- creator: z.string().describe('User ID who set the topic'),
570
- last_set: z.number().describe('Unix timestamp when topic was last set'),
571
- })
572
- .optional()
573
- .describe('Channel topic information'),
574
- purpose: z
575
- .object({
576
- value: z.string().describe('Purpose text'),
577
- creator: z.string().describe('User ID who set the purpose'),
578
- last_set: z
579
- .number()
580
- .describe('Unix timestamp when purpose was last set'),
581
- })
582
- .optional()
583
- .describe('Channel purpose information'),
584
- num_members: z
585
- .number()
586
- .optional()
587
- .describe('Number of members in the channel'),
588
- })
589
- .describe('Slack channel object with metadata');
590
- const SlackUserSchema = z
591
- .object({
592
- id: z.string().describe('Unique user identifier'),
593
- team_id: z.string().optional().describe('Team/workspace ID'),
594
- name: z.string().describe('Username (handle without @)'),
595
- deleted: z.boolean().optional().describe('True if user account is deleted'),
596
- color: z.string().optional().describe('Color code for user in UI'),
597
- real_name: z.string().optional().describe('Users real name'),
598
- tz: z.string().optional().describe('Timezone identifier'),
599
- tz_label: z.string().optional().describe('Human-readable timezone label'),
600
- tz_offset: z
601
- .number()
602
- .optional()
603
- .describe('Timezone offset from UTC in seconds'),
604
- profile: z
605
- .object({
606
- title: z.string().optional().describe('Job title'),
607
- phone: z.string().optional().describe('Phone number'),
608
- skype: z.string().optional().describe('Skype username'),
609
- real_name: z.string().optional().describe('Real name from profile'),
610
- real_name_normalized: z
611
- .string()
612
- .optional()
613
- .describe('Normalized real name'),
614
- display_name: z.string().optional().describe('Display name'),
615
- display_name_normalized: z
616
- .string()
617
- .optional()
618
- .describe('Normalized display name'),
619
- fields: z
620
- .record(z.unknown())
621
- .optional()
622
- .describe('Custom profile fields'),
623
- status_text: z.string().optional().describe('Current status text'),
624
- status_emoji: z.string().optional().describe('Current status emoji'),
625
- status_expiration: z
626
- .number()
627
- .optional()
628
- .describe('Unix timestamp when status expires'),
629
- avatar_hash: z.string().optional().describe('Hash for avatar image'),
630
- image_original: z
631
- .string()
632
- .optional()
633
- .describe('URL of original avatar image'),
634
- is_custom_image: z
635
- .boolean()
636
- .optional()
637
- .describe('True if using custom avatar'),
638
- email: z.string().optional().describe('Email address'),
639
- first_name: z.string().optional().describe('First name'),
640
- last_name: z.string().optional().describe('Last name'),
641
- image_24: z.string().optional().describe('24x24 pixel avatar URL'),
642
- image_32: z.string().optional().describe('32x32 pixel avatar URL'),
643
- image_48: z.string().optional().describe('48x48 pixel avatar URL'),
644
- image_72: z.string().optional().describe('72x72 pixel avatar URL'),
645
- image_192: z.string().optional().describe('192x192 pixel avatar URL'),
646
- image_512: z.string().optional().describe('512x512 pixel avatar URL'),
647
- image_1024: z
648
- .string()
649
- .optional()
650
- .describe('1024x1024 pixel avatar URL'),
651
- })
652
- .optional()
653
- .describe('User profile information'),
654
- is_admin: z
655
- .boolean()
656
- .optional()
657
- .describe('True if user is workspace admin'),
658
- is_owner: z
659
- .boolean()
660
- .optional()
661
- .describe('True if user is workspace owner'),
662
- is_primary_owner: z
663
- .boolean()
664
- .optional()
665
- .describe('True if user is primary workspace owner'),
666
- is_restricted: z
667
- .boolean()
668
- .optional()
669
- .describe('True if user is restricted (single-channel guest)'),
670
- is_ultra_restricted: z
671
- .boolean()
672
- .optional()
673
- .describe('True if user is ultra restricted (multi-channel guest)'),
674
- is_bot: z.boolean().optional().describe('True if this is a bot user'),
675
- is_app_user: z.boolean().optional().describe('True if this is an app user'),
676
- updated: z
677
- .number()
678
- .optional()
679
- .describe('Unix timestamp when user was last updated'),
680
- has_2fa: z
681
- .boolean()
682
- .optional()
683
- .describe('True if user has two-factor authentication enabled'),
684
- })
685
- .describe('Slack user object with profile and permissions');
686
- const SlackMessageSchema = z
687
- .object({
688
- type: z.string().describe('Message type (usually "message")'),
689
- ts: z.string().describe('Message timestamp (unique identifier)'),
690
- user: z.string().optional().describe('User ID who sent the message'),
691
- bot_id: z
692
- .string()
693
- .optional()
694
- .describe('Bot ID if message was sent by a bot'),
695
- bot_profile: z
696
- .object({
697
- name: z.string().optional().describe('Bot display name'),
698
- })
699
- .optional()
700
- .describe('Bot profile information if message was sent by a bot'),
701
- username: z
702
- .string()
703
- .optional()
704
- .describe('Username of the bot or user who sent the message'),
705
- text: z.string().optional().describe('Message text content'),
706
- thread_ts: z
707
- .string()
708
- .optional()
709
- .describe('Timestamp of parent message if this is a thread reply'),
710
- parent_user_id: z
711
- .string()
712
- .optional()
713
- .describe('User ID of thread parent message author'),
714
- reply_count: z
715
- .number()
716
- .optional()
717
- .describe('Number of replies in this thread'),
718
- reply_users_count: z
719
- .number()
720
- .optional()
721
- .describe('Number of unique users who replied in thread'),
722
- latest_reply: z
723
- .string()
724
- .optional()
725
- .describe('Timestamp of most recent reply in thread'),
726
- reply_users: z
727
- .array(z.string())
728
- .optional()
729
- .describe('Array of user IDs who replied in thread'),
730
- is_locked: z.boolean().optional().describe('True if thread is locked'),
731
- subscribed: z
732
- .boolean()
733
- .optional()
734
- .describe('True if current user is subscribed to thread'),
735
- attachments: z
736
- .array(z.unknown())
737
- .optional()
738
- .describe('Legacy message attachments'),
739
- blocks: z
740
- .array(z.unknown())
741
- .optional()
742
- .describe('Block Kit structured content'),
743
- reactions: z
744
- .array(z.object({
745
- name: z.string().describe('Emoji name without colons'),
746
- users: z
747
- .array(z.string())
748
- .describe('User IDs who reacted with this emoji'),
749
- count: z.number().describe('Total count of this reaction'),
750
- }))
751
- .optional()
752
- .describe('Array of emoji reactions on this message'),
753
- })
754
- .describe('Slack message object with content and metadata');
755
- // Define result schemas for different operations
756
- const SlackResultSchema = z.discriminatedUnion('operation', [
757
- z.object({
758
- operation: z
759
- .literal('send_message')
760
- .describe('Send a message to a Slack channel or DM'),
761
- ok: z.boolean().describe('Whether the Slack API call was successful'),
762
- channel: z
763
- .string()
764
- .optional()
765
- .describe('Channel ID where the message was sent'),
766
- ts: z.string().optional().describe('Timestamp of the sent message'),
767
- message: SlackMessageSchema.optional().describe('Details of the sent message'),
768
- error: z.string().describe('Error message if operation failed'),
769
- success: z.boolean().describe('Whether the operation was successful'),
770
- }),
771
- z.object({
772
- operation: z
773
- .literal('list_channels')
774
- .describe('List all channels in the Slack workspace'),
775
- ok: z.boolean().describe('Whether the Slack API call was successful'),
776
- channels: z
777
- .array(SlackChannelSchema)
778
- .optional()
779
- .describe('Array of channel objects'),
780
- response_metadata: z
781
- .object({
782
- next_cursor: z
783
- .string()
784
- .describe('Cursor for pagination to get next set of results'),
785
- })
786
- .optional()
787
- .describe('Metadata for pagination'),
788
- error: z.string().describe('Error message if operation failed'),
789
- success: z.boolean().describe('Whether the operation was successful'),
790
- }),
791
- z.object({
792
- operation: z
793
- .literal('get_channel_info')
794
- .describe('Get detailed information about a specific channel'),
795
- ok: z.boolean().describe('Whether the Slack API call was successful'),
796
- channel: SlackChannelSchema.optional().describe('Channel information object'),
797
- error: z.string().describe('Error message if operation failed'),
798
- success: z.boolean().describe('Whether the operation was successful'),
799
- }),
800
- z.object({
801
- operation: z
802
- .literal('get_user_info')
803
- .describe('Get detailed information about a specific user'),
804
- ok: z.boolean().describe('Whether the Slack API call was successful'),
805
- user: SlackUserSchema.optional().describe('User information object'),
806
- error: z.string().describe('Error message if operation failed'),
807
- success: z.boolean().describe('Whether the operation was successful'),
808
- }),
809
- z.object({
810
- operation: z
811
- .literal('list_users')
812
- .describe('List all users in the Slack workspace'),
813
- ok: z.boolean().describe('Whether the Slack API call was successful'),
814
- members: z
815
- .array(SlackUserSchema)
816
- .optional()
817
- .describe('Array of user objects'),
818
- response_metadata: z
819
- .object({
820
- next_cursor: z
821
- .string()
822
- .describe('Cursor for pagination to get next set of results'),
823
- })
824
- .optional()
825
- .describe('Metadata for pagination'),
826
- error: z.string().describe('Error message if operation failed'),
827
- success: z.boolean().describe('Whether the operation was successful'),
828
- }),
829
- z.object({
830
- operation: z
831
- .literal('get_conversation_history')
832
- .describe('Retrieve message history from a channel or direct message'),
833
- ok: z.boolean().describe('Whether the Slack API call was successful'),
834
- messages: z
835
- .array(SlackMessageSchema)
836
- .optional()
837
- .describe('Array of message objects'),
838
- has_more: z
839
- .boolean()
840
- .optional()
841
- .describe('Whether there are more messages to retrieve'),
842
- response_metadata: z
843
- .object({
844
- next_cursor: z
845
- .string()
846
- .describe('Cursor for pagination to get next set of results'),
847
- })
848
- .optional()
849
- .describe('Metadata for pagination'),
850
- error: z.string().describe('Error message if operation failed'),
851
- success: z.boolean().describe('Whether the operation was successful'),
852
- }),
853
- z.object({
854
- operation: z
855
- .literal('get_thread_replies')
856
- .describe('Retrieve all replies to a thread in a channel'),
857
- ok: z.boolean().describe('Whether the Slack API call was successful'),
858
- messages: z
859
- .array(SlackMessageSchema)
860
- .optional()
861
- .describe('Array of message objects in the thread'),
862
- has_more: z
863
- .boolean()
864
- .optional()
865
- .describe('Whether there are more messages to retrieve'),
866
- response_metadata: z
867
- .object({
868
- next_cursor: z
869
- .string()
870
- .describe('Cursor for pagination to get next set of results'),
871
- })
872
- .optional()
873
- .describe('Metadata for pagination'),
874
- error: z.string().describe('Error message if operation failed'),
875
- success: z.boolean().describe('Whether the operation was successful'),
876
- }),
877
- z.object({
878
- operation: z
879
- .literal('update_message')
880
- .describe('Update an existing message in a channel'),
881
- ok: z.boolean().describe('Whether the Slack API call was successful'),
882
- channel: z
883
- .string()
884
- .optional()
885
- .describe('Channel ID where the message was updated'),
886
- ts: z.string().optional().describe('Timestamp of the updated message'),
887
- text: z.string().optional().describe('Updated text content of the message'),
888
- message: SlackMessageSchema.optional().describe('Details of the updated message'),
889
- error: z.string().describe('Error message if operation failed'),
890
- success: z.boolean().describe('Whether the operation was successful'),
891
- }),
892
- z.object({
893
- operation: z
894
- .literal('delete_message')
895
- .describe('Delete a message from a channel'),
896
- ok: z.boolean().describe('Whether the Slack API call was successful'),
897
- channel: z
898
- .string()
899
- .optional()
900
- .describe('Channel ID where the message was deleted'),
901
- ts: z.string().optional().describe('Timestamp of the deleted message'),
902
- error: z.string().describe('Error message if operation failed'),
903
- success: z.boolean().describe('Whether the operation was successful'),
904
- }),
905
- z.object({
906
- operation: z
907
- .literal('add_reaction')
908
- .describe('Add an emoji reaction to a message'),
909
- ok: z.boolean().describe('Whether the Slack API call was successful'),
910
- error: z.string().describe('Error message if operation failed'),
911
- success: z.boolean().describe('Whether the operation was successful'),
912
- }),
913
- z.object({
914
- operation: z
915
- .literal('remove_reaction')
916
- .describe('Remove an emoji reaction from a message'),
917
- ok: z.boolean().describe('Whether the Slack API call was successful'),
918
- error: z.string().describe('Error message if operation failed'),
919
- success: z.boolean().describe('Whether the operation was successful'),
920
- }),
921
- z.object({
922
- operation: z
923
- .literal('join_channel')
924
- .describe('Join a public Slack channel'),
925
- ok: z.boolean().describe('Whether the Slack API call was successful'),
926
- channel: SlackChannelSchema.optional().describe('Channel information object after joining'),
927
- already_in_channel: z
928
- .boolean()
929
- .optional()
930
- .describe('Whether the bot was already a member of the channel'),
931
- error: z.string().describe('Error message if operation failed'),
932
- success: z.boolean().describe('Whether the operation was successful'),
933
- }),
934
- z.object({
935
- operation: z
936
- .literal('upload_file')
937
- .describe('Upload a file to a Slack channel'),
938
- ok: z.boolean().describe('Whether the Slack API call was successful'),
939
- file: z
940
- .object({
941
- id: z.string().describe('Unique file identifier'),
942
- created: z.number().describe('Unix timestamp when file was created'),
943
- timestamp: z.number().describe('Unix timestamp when file was uploaded'),
944
- name: z.string().describe('Original filename'),
945
- title: z.string().optional().describe('File title'),
946
- mimetype: z.string().describe('MIME type of the file'),
947
- filetype: z.string().describe('File type extension'),
948
- pretty_type: z.string().describe('Human-readable file type'),
949
- user: z.string().describe('User ID who uploaded the file'),
950
- editable: z.boolean().describe('Whether the file is editable'),
951
- size: z.number().describe('File size in bytes'),
952
- mode: z.string().describe('File sharing mode'),
953
- is_external: z
954
- .boolean()
955
- .describe('Whether file is from external source'),
956
- external_type: z.string().describe('External file type if applicable'),
957
- is_public: z.boolean().describe('Whether file is publicly accessible'),
958
- public_url_shared: z.boolean().describe('Whether public URL is shared'),
959
- display_as_bot: z
960
- .boolean()
961
- .describe('Whether file is displayed as uploaded by bot'),
962
- username: z.string().describe('Username of uploader'),
963
- url_private: z.string().describe('Private URL to access file'),
964
- url_private_download: z.string().describe('Private download URL'),
965
- permalink: z.string().describe('Permanent link to file'),
966
- permalink_public: z
967
- .string()
968
- .optional()
969
- .describe('Public permanent link'),
970
- shares: z
971
- .object({
972
- public: z
973
- .record(z.array(z.object({
974
- reply_users: z
975
- .array(z.string())
976
- .describe('User IDs who replied'),
977
- reply_users_count: z
978
- .number()
979
- .describe('Number of unique users who replied'),
980
- reply_count: z.number().describe('Total number of replies'),
981
- ts: z.string().describe('Timestamp of the share'),
982
- channel_name: z.string().describe('Name of the channel'),
983
- team_id: z.string().describe('Team ID'),
984
- })))
985
- .optional()
986
- .describe('Public channel shares'),
987
- private: z
988
- .record(z.array(z.object({
989
- reply_users: z
990
- .array(z.string())
991
- .describe('User IDs who replied'),
992
- reply_users_count: z
993
- .number()
994
- .describe('Number of unique users who replied'),
995
- reply_count: z.number().describe('Total number of replies'),
996
- ts: z.string().describe('Timestamp of the share'),
997
- channel_name: z.string().describe('Name of the channel'),
998
- team_id: z.string().describe('Team ID'),
999
- })))
1000
- .optional()
1001
- .describe('Private channel shares'),
1002
- })
1003
- .optional()
1004
- .describe('Information about where file is shared'),
1005
- channels: z
1006
- .array(z.string())
1007
- .optional()
1008
- .describe('Channel IDs where file is shared'),
1009
- groups: z
1010
- .array(z.string())
1011
- .optional()
1012
- .describe('Private group IDs where file is shared'),
1013
- ims: z
1014
- .array(z.string())
1015
- .optional()
1016
- .describe('Direct message IDs where file is shared'),
1017
- has_rich_preview: z
1018
- .boolean()
1019
- .optional()
1020
- .describe('Whether file has rich preview'),
1021
- })
1022
- .optional()
1023
- .describe('File information object'),
1024
- error: z.string().describe('Error message if operation failed'),
1025
- success: z.boolean().describe('Whether the operation was successful'),
1026
- }),
1027
- // Schedule message result
1028
- z.object({
1029
- operation: z
1030
- .literal('schedule_message')
1031
- .describe('Schedule a message to be sent at a future time'),
1032
- ok: z.boolean().describe('Whether the Slack API call was successful'),
1033
- channel: z
1034
- .string()
1035
- .optional()
1036
- .describe('Channel ID where message will be posted'),
1037
- scheduled_message_id: z
1038
- .string()
1039
- .optional()
1040
- .describe('Unique identifier for the scheduled message'),
1041
- post_at: z
1042
- .number()
1043
- .optional()
1044
- .describe('Unix timestamp when message will be posted'),
1045
- error: z.string().describe('Error message if operation failed'),
1046
- success: z.boolean().describe('Whether the operation was successful'),
1047
- }),
1048
- ]);
1049
- export class SlackBubble extends ServiceBubble {
1050
- async testCredential() {
1051
- // Make a test API call to the Slack API
1052
- const response = await this.makeSlackApiCall('auth.test', {});
1053
- if (response.ok) {
1054
- return true;
1055
- }
1056
- return false;
1057
- }
1058
- static type = 'service';
1059
- static service = 'slack';
1060
- static authType = 'apikey';
1061
- static bubbleName = 'slack';
1062
- static schema = SlackParamsSchema;
1063
- static resultSchema = SlackResultSchema;
1064
- static shortDescription = 'Slack integration for messaging and workspace management';
1065
- static longDescription = `
1066
- Comprehensive Slack integration for messaging and workspace management.
1067
- Supports both Bot tokens (xoxb-) and User tokens (xoxp-).
1068
-
1069
- ## Token Types: Bot vs User
1070
-
1071
- | Aspect | Bot Token (xoxb-) | User Token (xoxp-) |
1072
- |--------|-------------------|-------------------|
1073
- | Identity | Acts as the bot | Acts as the authorizing user |
1074
- | Channel access | Only channels bot is added to | All channels user can access |
1075
- | Message deletion | Can only delete bot's own messages | Can delete any message user has permission for |
1076
- | Message posting | Messages appear from the bot | Messages appear from the user |
1077
- | Scope location | "Bot Token Scopes" section | "User Token Scopes" section |
1078
-
1079
- Choose **Bot token** for: Automations, notifications, bots that act independently
1080
- Choose **User token** for: Acting on behalf of a user, accessing user's private channels
1081
-
1082
- ## Required OAuth Scopes by Operation
1083
-
1084
- Configure in your Slack App: OAuth & Permissions page
1085
- Official docs: https://docs.slack.dev/reference/scopes/
1086
-
1087
- ### Messaging Operations
1088
- | Operation | Bot Token Scope | User Token Scope |
1089
- |------------------|-----------------|------------------|
1090
- | send_message | chat:write (+ chat:write.public for any public channel) | chat:write |
1091
- | send_message (to user DM) | chat:write + im:write | chat:write + im:write |
1092
- | schedule_message | chat:write | chat:write |
1093
- | update_message | chat:write | chat:write |
1094
- | delete_message | chat:write (own messages only) | chat:write (any deletable) |
1095
-
1096
- **Note on DMs**: When you pass a user ID (e.g., U12345678) as the channel, SlackBubble automatically opens a DM conversation with that user. This requires the \`im:write\` scope in addition to \`chat:write\`.
1097
-
1098
- ### Channel & Conversation Operations
1099
- | Operation | Bot Token Scope | User Token Scope |
1100
- |--------------------------|-----------------|------------------|
1101
- | list_channels | channels:read, groups:read | channels:read, groups:read |
1102
- | get_channel_info | channels:read OR groups:read | channels:read OR groups:read |
1103
- | join_channel | channels:join | channels:write |
1104
- | get_conversation_history | channels:history, groups:history | channels:history, groups:history |
1105
- | get_thread_replies | channels:history, groups:history | channels:history, groups:history |
1106
-
1107
- ### User Operations
1108
- | Operation | Bot Token Scope | User Token Scope |
1109
- |---------------|-----------------|------------------|
1110
- | list_users | users:read | users:read |
1111
- | get_user_info | users:read | users:read |
1112
- | (email field) | + users:read.email | + users:read.email |
1113
-
1114
- ### Reaction & File Operations
1115
- | Operation | Bot Token Scope | User Token Scope |
1116
- |-----------------|-----------------|------------------|
1117
- | add_reaction | reactions:write | reactions:write |
1118
- | remove_reaction | reactions:write | reactions:write |
1119
- | upload_file | files:write | files:write |
1120
-
1121
- ### Direct Message (DM) Scopes
1122
- For operations on DMs and group DMs, add these additional scopes:
1123
- | Scope | Purpose |
1124
- |-------|---------|
1125
- | im:read | Access direct message channel info |
1126
- | im:write | Start direct message conversations |
1127
- | im:history | Read direct message history |
1128
- | mpim:read | Access group DM channel info |
1129
- | mpim:write | Start group DM conversations |
1130
- | mpim:history | Read group DM history |
1131
-
1132
- ## Quick Setup Guide
1133
-
1134
- ### For Bot Tokens (xoxb-)
1135
- 1. Go to https://api.slack.com/apps → select your app
1136
- 2. Navigate to "OAuth & Permissions"
1137
- 3. Scroll to "Bot Token Scopes" section → add required scopes
1138
- 4. Click "Install to Workspace" (or "Reinstall" if updating)
1139
- 5. Copy "Bot User OAuth Token" (starts with xoxb-)
1140
-
1141
- ### For User Tokens (xoxp-)
1142
- 1. Go to https://api.slack.com/apps → select your app
1143
- 2. Navigate to "OAuth & Permissions"
1144
- 3. Scroll to "User Token Scopes" section → add required scopes
1145
- 4. Click "Install to Workspace" (or "Reinstall" if updating)
1146
- 5. Copy "User OAuth Token" (starts with xoxp-)
1147
-
1148
- ## Minimum Recommended Scopes
1149
- For Bot Token: chat:write, channels:read, groups:read, users:read, channels:history
1150
- For User Token: chat:write, channels:read, groups:read, users:read, channels:history, channels:write
1151
-
1152
- ## Setting Up Slack Triggers (Event Subscriptions)
1153
-
1154
- To trigger BubbleFlow workflows from Slack events (like @mentions), you need to configure Event Subscriptions.
1155
- Official docs: https://docs.slack.dev/apis/events-api/
1156
-
1157
- ### Supported Trigger Events
1158
- | Trigger Type | Slack Event | Required Scope |
1159
- |--------------|-------------|----------------|
1160
- | slack/bot_mentioned | app_mention | app_mentions:read |
1161
-
1162
- ### Step-by-Step Event Subscriptions Setup
1163
-
1164
- **Step 1: Get your webhook URL from Bubble Lab**
1165
- - In Bubble Lab, create a flow with a Slack trigger (e.g., slack/bot_mentioned)
1166
- - Copy the webhook URL provided (format: https://api.bubblelab.ai/webhook/{userId}/{path})
1167
-
1168
- **Step 2: Enable Event Subscriptions in Slack**
1169
- 1. Go to https://api.slack.com/apps → select your app
1170
- 2. Click "Event Subscriptions" in the left sidebar
1171
- 3. Toggle "Enable Events" to ON
1172
-
1173
- **Step 3: Configure Request URL**
1174
- 1. Paste your Bubble Lab webhook URL in the "Request URL" field
1175
- 2. Slack will send a verification challenge to your URL
1176
- 3. Wait for the green "Verified" checkmark (Bubble Lab handles verification automatically)
1177
- 4. If verification fails, click "Retry" (your server may need a moment to respond)
1178
-
1179
- **Step 4: Subscribe to Bot Events**
1180
- 1. Scroll down to "Subscribe to bot events"
1181
- 2. Click "Add Bot User Event"
1182
- 3. Add the events you need:
1183
- - For @mentions: add "app_mention"
1184
- 4. Click "Save Changes"
1185
-
1186
- **Step 5: Add Required OAuth Scopes**
1187
- 1. Go to "OAuth & Permissions" in the sidebar
1188
- 2. Under "Bot Token Scopes", add:
1189
- - app_mentions:read (for app_mention events)
1190
- 3. Click "Save"
1191
-
1192
- **Step 6: Reinstall Your App**
1193
- 1. Go to "Install App" in the sidebar
1194
- 2. Click "Reinstall to Workspace"
1195
- 3. Authorize the new permissions
1196
-
1197
- ### Troubleshooting Event Subscriptions
1198
- - **Verification failed**: Ensure your webhook URL is correct and accessible
1199
- - **Not receiving events**: Check that you added the correct scopes AND reinstalled the app
1200
- - **Bot not responding**: Make sure the bot is invited to the channel where it's mentioned
1201
- `;
1202
- static alias = 'slack';
1203
- constructor(params = {
1204
- operation: 'list_channels',
1205
- }, context, instanceId) {
1206
- super(params, context, instanceId);
1207
- }
1208
- async performAction(context) {
1209
- // Context is available but not currently used in this implementation
1210
- void context;
1211
- const { operation } = this.params;
1212
- try {
1213
- const result = await (async () => {
1214
- switch (operation) {
1215
- case 'send_message':
1216
- return await this.sendMessage(this.params);
1217
- case 'list_channels':
1218
- return await this.listChannels(this.params);
1219
- case 'get_channel_info':
1220
- return await this.getChannelInfo(this.params);
1221
- case 'get_user_info':
1222
- return await this.getUserInfo(this.params);
1223
- case 'list_users':
1224
- return await this.listUsers(this.params);
1225
- case 'get_conversation_history':
1226
- return await this.getConversationHistory(this.params);
1227
- case 'get_thread_replies':
1228
- return await this.getThreadReplies(this.params);
1229
- case 'update_message':
1230
- return await this.updateMessage(this.params);
1231
- case 'delete_message':
1232
- return await this.deleteMessage(this.params);
1233
- case 'add_reaction':
1234
- return await this.addReaction(this.params);
1235
- case 'remove_reaction':
1236
- return await this.removeReaction(this.params);
1237
- case 'upload_file':
1238
- return await this.uploadFile(this.params);
1239
- case 'join_channel':
1240
- return await this.joinChannel(this.params);
1241
- case 'schedule_message':
1242
- return await this.scheduleMessage(this.params);
1243
- default:
1244
- throw new Error(`Unsupported operation: ${operation}`);
1245
- }
1246
- })();
1247
- // The result is guaranteed to match T['operation'] because of the discriminated union
1248
- return result;
1249
- }
1250
- catch (error) {
1251
- const failedOperation = this.params.operation;
1252
- return {
1253
- success: false,
1254
- ok: false,
1255
- operation: failedOperation,
1256
- error: error instanceof Error
1257
- ? error.message
1258
- : 'Unknown error occurred in SlackBubble',
1259
- };
1260
- }
1261
- }
1262
- /**
1263
- * Helper method to resolve channel names to channel IDs.
1264
- * If the input looks like a channel ID (starts with C, G, or D), returns it as-is.
1265
- * Otherwise, searches for a channel with the given name.
1266
- */
1267
- async resolveChannelId(channelInput) {
1268
- // Check if input is already a channel ID (starts with C, G, D, etc.)
1269
- if (/^[CGD][A-Z0-9]+$/i.test(channelInput)) {
1270
- return channelInput;
1271
- }
1272
- // Check if input is a user ID (starts with U or W for enterprise users)
1273
- // If so, open a DM conversation with them to get the DM channel ID
1274
- if (/^[UW][A-Z0-9]+$/i.test(channelInput)) {
1275
- const dmChannel = await this.openDmConversation(channelInput);
1276
- return dmChannel;
1277
- }
1278
- // Remove # prefix if present
1279
- const channelName = channelInput.replace(/^#/, '');
1280
- // Get all channels to find the matching name
1281
- const response = await this.makeSlackApiCall('conversations.list', {
1282
- types: 'public_channel,private_channel',
1283
- exclude_archived: 'true',
1284
- limit: '1000', // Get a large batch to find the channel
1285
- }, 'GET');
1286
- if (!response.ok) {
1287
- throw new Error(`Failed to list channels: ${response.error}`);
1288
- }
1289
- const channels = response.channels;
1290
- const matchedChannel = channels.find((channel) => channel.name.toLowerCase() === channelName.toLowerCase());
1291
- if (!matchedChannel) {
1292
- throw new Error(`Channel "${channelName}" not found. Available channels: ${channels.map((c) => c.name).join(', ')}`);
1293
- }
1294
- return matchedChannel.id;
1295
- }
1296
- async sendMessage(params) {
1297
- const { channel, text, username, icon_emoji, icon_url, attachments, blocks, thread_ts, reply_broadcast, unfurl_links, unfurl_media, } = params;
1298
- // Resolve channel name to ID if needed
1299
- const resolvedChannel = await this.resolveChannelId(channel);
1300
- const body = {
1301
- channel: resolvedChannel,
1302
- text,
1303
- unfurl_links,
1304
- unfurl_media,
1305
- };
1306
- if (username)
1307
- body.username = username;
1308
- if (icon_emoji)
1309
- body.icon_emoji = icon_emoji;
1310
- if (icon_url)
1311
- body.icon_url = icon_url;
1312
- if (attachments)
1313
- body.attachments = JSON.stringify(attachments);
1314
- if (blocks)
1315
- body.blocks = JSON.stringify(blocks);
1316
- if (thread_ts) {
1317
- body.thread_ts = thread_ts;
1318
- body.reply_broadcast = reply_broadcast;
1319
- }
1320
- console.log('sending blocks', body.blocks);
1321
- const response = await this.makeSlackApiCall('chat.postMessage', body);
1322
- return {
1323
- operation: 'send_message',
1324
- ok: response.ok,
1325
- channel: response.ok ? response.channel : undefined,
1326
- ts: response.ok ? response.ts : undefined,
1327
- message: response.ok && response.message
1328
- ? SlackMessageSchema.parse(response.message)
1329
- : undefined,
1330
- error: !response.ok ? JSON.stringify(response, null, 2) : '',
1331
- success: response.ok,
1332
- };
1333
- }
1334
- async listChannels(params) {
1335
- // Parse the params to apply defaults
1336
- const parsed = SlackParamsSchema.parse(params);
1337
- const { types, exclude_archived, limit, cursor } = parsed;
1338
- const queryParams = {
1339
- types: types.join(','),
1340
- exclude_archived: exclude_archived.toString(),
1341
- limit: limit.toString(),
1342
- };
1343
- if (cursor)
1344
- queryParams.cursor = cursor;
1345
- const response = await this.makeSlackApiCall('conversations.list', queryParams, 'GET');
1346
- return {
1347
- operation: 'list_channels',
1348
- ok: response.ok,
1349
- channels: response.ok && response.channels
1350
- ? z.array(SlackChannelSchema).parse(response.channels)
1351
- : undefined,
1352
- response_metadata: response.ok && response.response_metadata
1353
- ? {
1354
- next_cursor: response.response_metadata.next_cursor,
1355
- }
1356
- : undefined,
1357
- error: !response.ok ? JSON.stringify(response, null, 2) : '',
1358
- success: response.ok,
1359
- };
1360
- }
1361
- async getChannelInfo(params) {
1362
- // Parse the params to apply defaults
1363
- const parsed = SlackParamsSchema.parse(params);
1364
- const { channel, include_locale } = parsed;
1365
- // Resolve channel name to ID if needed
1366
- const resolvedChannel = await this.resolveChannelId(channel);
1367
- const queryParams = {
1368
- channel: resolvedChannel,
1369
- include_locale: include_locale.toString(),
1370
- };
1371
- const response = await this.makeSlackApiCall('conversations.info', queryParams, 'GET');
1372
- return {
1373
- operation: 'get_channel_info',
1374
- ok: response.ok,
1375
- channel: response.ok && response.channel
1376
- ? SlackChannelSchema.parse(response.channel)
1377
- : undefined,
1378
- error: !response.ok ? JSON.stringify(response, null, 2) : '',
1379
- success: response.ok,
1380
- };
1381
- }
1382
- async getUserInfo(params) {
1383
- // Parse the params to apply defaults
1384
- const parsed = SlackParamsSchema.parse(params);
1385
- const { user, include_locale } = parsed;
1386
- const queryParams = {
1387
- user,
1388
- include_locale: include_locale.toString(),
1389
- };
1390
- const response = await this.makeSlackApiCall('users.info', queryParams, 'GET');
1391
- return {
1392
- operation: 'get_user_info',
1393
- ok: response.ok,
1394
- user: response.ok && response.user
1395
- ? SlackUserSchema.parse(response.user)
1396
- : undefined,
1397
- error: !response.ok ? JSON.stringify(response, null, 2) : '',
1398
- success: response.ok,
1399
- };
1400
- }
1401
- async listUsers(params) {
1402
- // Parse the params to apply defaults
1403
- const parsed = SlackParamsSchema.parse(params);
1404
- const { limit, cursor, include_locale } = parsed;
1405
- const queryParams = {
1406
- limit: limit.toString(),
1407
- include_locale: include_locale.toString(),
1408
- };
1409
- if (cursor)
1410
- queryParams.cursor = cursor;
1411
- const response = await this.makeSlackApiCall('users.list', queryParams, 'GET');
1412
- return {
1413
- operation: 'list_users',
1414
- ok: response.ok,
1415
- members: response.ok && response.members
1416
- ? z.array(SlackUserSchema).parse(response.members)
1417
- : undefined,
1418
- response_metadata: response.ok && response.response_metadata
1419
- ? {
1420
- next_cursor: response.response_metadata.next_cursor,
1421
- }
1422
- : undefined,
1423
- error: !response.ok ? JSON.stringify(response, null, 2) : '',
1424
- success: response.ok,
1425
- };
1426
- }
1427
- async getConversationHistory(params) {
1428
- // Parse the params to apply defaults
1429
- const parsed = SlackParamsSchema.parse(params);
1430
- const { channel, latest, oldest, inclusive, limit, cursor } = parsed;
1431
- // Resolve channel name to ID if needed
1432
- const resolvedChannel = await this.resolveChannelId(channel);
1433
- const queryParams = {
1434
- channel: resolvedChannel,
1435
- inclusive: inclusive.toString(),
1436
- limit: limit.toString(),
1437
- };
1438
- if (latest)
1439
- queryParams.latest = latest;
1440
- if (oldest)
1441
- queryParams.oldest = oldest;
1442
- if (cursor)
1443
- queryParams.cursor = cursor;
1444
- const response = await this.makeSlackApiCall('conversations.history', queryParams, 'GET');
1445
- return {
1446
- operation: 'get_conversation_history',
1447
- ok: response.ok,
1448
- messages: response.ok && response.messages
1449
- ? z.array(SlackMessageSchema).parse(response.messages)
1450
- : undefined,
1451
- has_more: response.ok ? response.has_more : undefined,
1452
- response_metadata: response.ok && response.response_metadata
1453
- ? {
1454
- next_cursor: response.response_metadata.next_cursor,
1455
- }
1456
- : undefined,
1457
- error: !response.ok ? JSON.stringify(response, null, 2) : '',
1458
- success: response.ok,
1459
- };
1460
- }
1461
- async getThreadReplies(params) {
1462
- // Parse the params to apply defaults
1463
- const parsed = SlackParamsSchema.parse(params);
1464
- const { channel, ts, latest, oldest, inclusive, limit, cursor } = parsed;
1465
- // Resolve channel name to ID if needed
1466
- const resolvedChannel = await this.resolveChannelId(channel);
1467
- const queryParams = {
1468
- channel: resolvedChannel,
1469
- ts: ts,
1470
- };
1471
- if (latest)
1472
- queryParams.latest = latest;
1473
- if (oldest)
1474
- queryParams.oldest = oldest;
1475
- if (inclusive !== undefined)
1476
- queryParams.inclusive = inclusive.toString();
1477
- if (limit !== undefined)
1478
- queryParams.limit = limit.toString();
1479
- if (cursor)
1480
- queryParams.cursor = cursor;
1481
- const response = await this.makeSlackApiCall('conversations.replies', queryParams, 'GET');
1482
- return {
1483
- operation: 'get_thread_replies',
1484
- ok: response.ok,
1485
- messages: response.ok && response.messages
1486
- ? z.array(SlackMessageSchema).parse(response.messages)
1487
- : undefined,
1488
- has_more: response.ok ? response.has_more : undefined,
1489
- response_metadata: response.ok && response.response_metadata
1490
- ? {
1491
- next_cursor: response.response_metadata.next_cursor,
1492
- }
1493
- : undefined,
1494
- error: !response.ok ? JSON.stringify(response, null, 2) : '',
1495
- success: response.ok,
1496
- };
1497
- }
1498
- async updateMessage(params) {
1499
- const { channel, ts, text, attachments, blocks } = params;
1500
- // Resolve channel name to ID if needed
1501
- const resolvedChannel = await this.resolveChannelId(channel);
1502
- const body = {
1503
- channel: resolvedChannel,
1504
- ts,
1505
- };
1506
- if (text)
1507
- body.text = text;
1508
- if (attachments)
1509
- body.attachments = JSON.stringify(attachments);
1510
- if (blocks)
1511
- body.blocks = JSON.stringify(blocks);
1512
- const response = await this.makeSlackApiCall('chat.update', body);
1513
- return {
1514
- operation: 'update_message',
1515
- ok: response.ok,
1516
- channel: response.ok ? response.channel : undefined,
1517
- ts: response.ok ? response.ts : undefined,
1518
- text: response.ok ? response.text : undefined,
1519
- message: response.ok && response.message
1520
- ? SlackMessageSchema.parse(response.message)
1521
- : undefined,
1522
- error: !response.ok ? JSON.stringify(response, null, 2) : '',
1523
- success: response.ok,
1524
- };
1525
- }
1526
- async deleteMessage(params) {
1527
- const { channel, ts } = params;
1528
- // Resolve channel name to ID if needed
1529
- const resolvedChannel = await this.resolveChannelId(channel);
1530
- const body = {
1531
- channel: resolvedChannel,
1532
- ts,
1533
- };
1534
- const response = await this.makeSlackApiCall('chat.delete', body);
1535
- return {
1536
- operation: 'delete_message',
1537
- ok: response.ok,
1538
- channel: response.ok ? response.channel : undefined,
1539
- ts: response.ok ? response.ts : undefined,
1540
- error: !response.ok ? JSON.stringify(response, null, 2) : '',
1541
- success: response.ok,
1542
- };
1543
- }
1544
- async addReaction(params) {
1545
- const { name, channel, timestamp } = params;
1546
- // Resolve channel name to ID if needed
1547
- const resolvedChannel = await this.resolveChannelId(channel);
1548
- const body = {
1549
- name,
1550
- channel: resolvedChannel,
1551
- timestamp,
1552
- };
1553
- const response = await this.makeSlackApiCall('reactions.add', body);
1554
- return {
1555
- operation: 'add_reaction',
1556
- ok: response.ok,
1557
- error: !response.ok ? JSON.stringify(response, null, 2) : '',
1558
- success: response.ok,
1559
- };
1560
- }
1561
- async removeReaction(params) {
1562
- const { name, channel, timestamp } = params;
1563
- // Resolve channel name to ID if needed
1564
- const resolvedChannel = await this.resolveChannelId(channel);
1565
- const body = {
1566
- name,
1567
- channel: resolvedChannel,
1568
- timestamp,
1569
- };
1570
- const response = await this.makeSlackApiCall('reactions.remove', body);
1571
- return {
1572
- operation: 'remove_reaction',
1573
- ok: response.ok,
1574
- error: !response.ok ? JSON.stringify(response, null, 2) : '',
1575
- success: response.ok,
1576
- };
1577
- }
1578
- async uploadFile(params) {
1579
- const { channel, file_path, filename, title, initial_comment, thread_ts } = params;
1580
- // Resolve channel name to ID if needed
1581
- const resolvedChannel = await this.resolveChannelId(channel);
1582
- // Read the file
1583
- const fs = await import('fs/promises');
1584
- const path = await import('path');
1585
- try {
1586
- const fileBuffer = await fs.readFile(file_path);
1587
- const actualFilename = filename || path.basename(file_path);
1588
- const fileSize = fileBuffer.length;
1589
- // Step 1: Get upload URL
1590
- const uploadUrlResponse = await this.makeSlackApiCall('files.getUploadURLExternal', {
1591
- filename: actualFilename,
1592
- length: fileSize.toString(),
1593
- });
1594
- if (!uploadUrlResponse.ok) {
1595
- throw new Error(`Failed to get upload URL: ${uploadUrlResponse.error}`);
1596
- }
1597
- const { upload_url, file_id } = uploadUrlResponse;
1598
- // Step 2: Upload file to the URL
1599
- const uploadResponse = await fetch(upload_url, {
1600
- method: 'POST',
1601
- body: fileBuffer,
1602
- headers: {
1603
- 'Content-Type': 'application/octet-stream',
1604
- },
1605
- });
1606
- if (!uploadResponse.ok) {
1607
- throw new Error(`Failed to upload file: ${uploadResponse.statusText}`);
1608
- }
1609
- // Step 3: Complete the upload
1610
- const completeParams = {
1611
- files: JSON.stringify([
1612
- {
1613
- id: file_id,
1614
- title: title || actualFilename,
1615
- },
1616
- ]),
1617
- };
1618
- // Add optional parameters
1619
- if (resolvedChannel)
1620
- completeParams.channel_id = resolvedChannel;
1621
- if (initial_comment)
1622
- completeParams.initial_comment = initial_comment;
1623
- if (thread_ts)
1624
- completeParams.thread_ts = thread_ts;
1625
- const completeResponse = await this.makeSlackApiCall('files.completeUploadExternal', completeParams);
1626
- if (!completeResponse.ok) {
1627
- throw new Error(`Failed to complete upload: ${completeResponse.error}`);
1628
- }
1629
- // Extract file info from response
1630
- const files = completeResponse.files || [];
1631
- const uploadedFile = files[0] || {};
1632
- return {
1633
- operation: 'upload_file',
1634
- ok: true,
1635
- file: {
1636
- id: uploadedFile.id || file_id,
1637
- created: uploadedFile.created || Date.now() / 1000,
1638
- timestamp: uploadedFile.timestamp || Date.now() / 1000,
1639
- name: uploadedFile.name || actualFilename,
1640
- title: uploadedFile.title || title || actualFilename,
1641
- mimetype: uploadedFile.mimetype || 'image/png',
1642
- filetype: uploadedFile.filetype || 'png',
1643
- pretty_type: uploadedFile.pretty_type || 'PNG',
1644
- user: uploadedFile.user || '',
1645
- editable: uploadedFile.editable || false,
1646
- size: uploadedFile.size || fileSize,
1647
- mode: uploadedFile.mode || 'hosted',
1648
- is_external: uploadedFile.is_external || false,
1649
- external_type: uploadedFile.external_type || '',
1650
- is_public: uploadedFile.is_public || false,
1651
- public_url_shared: uploadedFile.public_url_shared || false,
1652
- display_as_bot: uploadedFile.display_as_bot || false,
1653
- username: uploadedFile.username || '',
1654
- url_private: uploadedFile.url_private || '',
1655
- url_private_download: uploadedFile.url_private_download || '',
1656
- permalink: uploadedFile.permalink || '',
1657
- permalink_public: uploadedFile.permalink_public || '',
1658
- shares: uploadedFile.shares || {},
1659
- channels: uploadedFile.channels || [resolvedChannel],
1660
- groups: uploadedFile.groups || [],
1661
- ims: uploadedFile.ims || [],
1662
- has_rich_preview: uploadedFile.has_rich_preview || false,
1663
- },
1664
- error: '',
1665
- success: true,
1666
- };
1667
- }
1668
- catch (error) {
1669
- return {
1670
- operation: 'upload_file',
1671
- ok: false,
1672
- error: error instanceof Error ? error.message : 'Unknown file upload error',
1673
- success: false,
1674
- };
1675
- }
1676
- }
1677
- async joinChannel(params) {
1678
- const { channel } = params;
1679
- // Resolve channel name to ID if needed
1680
- const resolvedChannel = await this.resolveChannelId(channel);
1681
- const body = {
1682
- channel: resolvedChannel,
1683
- };
1684
- const response = await this.makeSlackApiCall('conversations.join', body);
1685
- return {
1686
- operation: 'join_channel',
1687
- ok: response.ok,
1688
- channel: response.ok && response.channel
1689
- ? SlackChannelSchema.parse(response.channel)
1690
- : undefined,
1691
- already_in_channel: response.ok
1692
- ? response.already_in_channel
1693
- : undefined,
1694
- error: !response.ok ? JSON.stringify(response, null, 2) : '',
1695
- success: response.ok,
1696
- };
1697
- }
1698
- async scheduleMessage(params) {
1699
- const { channel, text, post_at, thread_ts, blocks, unfurl_links, unfurl_media, } = params;
1700
- // Resolve channel name to ID if needed
1701
- const resolvedChannel = await this.resolveChannelId(channel);
1702
- const body = {
1703
- channel: resolvedChannel,
1704
- text,
1705
- post_at,
1706
- };
1707
- if (thread_ts)
1708
- body.thread_ts = thread_ts;
1709
- if (blocks)
1710
- body.blocks = JSON.stringify(blocks);
1711
- if (unfurl_links !== undefined)
1712
- body.unfurl_links = unfurl_links;
1713
- if (unfurl_media !== undefined)
1714
- body.unfurl_media = unfurl_media;
1715
- const response = await this.makeSlackApiCall('chat.scheduleMessage', body);
1716
- return {
1717
- operation: 'schedule_message',
1718
- ok: response.ok,
1719
- channel: response.ok
1720
- ? response.channel
1721
- : undefined,
1722
- scheduled_message_id: response.ok
1723
- ? response.scheduled_message_id
1724
- : undefined,
1725
- post_at: response.ok
1726
- ? response.post_at
1727
- : undefined,
1728
- error: !response.ok ? JSON.stringify(response, null, 2) : '',
1729
- success: response.ok,
1730
- };
1731
- }
1732
- /**
1733
- * Opens a DM conversation with a user and returns the DM channel ID.
1734
- * Required scope: im:write (for bot tokens) or im:write (for user tokens)
1735
- */
1736
- async openDmConversation(userId) {
1737
- const response = await this.makeSlackApiCall('conversations.open', {
1738
- users: userId,
1739
- });
1740
- if (!response.ok) {
1741
- throw new Error(`Failed to open DM with user ${userId}: ${response.error}. ` +
1742
- `Make sure you have the 'im:write' scope enabled in your Slack app.`);
1743
- }
1744
- const channel = response.channel;
1745
- if (!channel?.id) {
1746
- throw new Error(`Failed to get DM channel ID for user ${userId}. Unexpected API response.`);
1747
- }
1748
- return channel.id;
1749
- }
1750
- chooseCredential() {
1751
- const { credentials } = this.params;
1752
- // If no credentials were injected, return undefined
1753
- if (!credentials || typeof credentials !== 'object') {
1754
- throw new Error('No slack credentials provided');
1755
- }
1756
- // Slack bubble always uses Slack credentials
1757
- return credentials[CredentialType.SLACK_CRED];
1758
- }
1759
- async makeSlackApiCall(endpoint, params, method = 'POST') {
1760
- const url = `${SLACK_API_BASE}/${endpoint}`;
1761
- // Use chooseCredential to get the appropriate credential
1762
- const authToken = this.chooseCredential();
1763
- if (!authToken) {
1764
- throw new Error('Slack authentication token is required but was not provided');
1765
- }
1766
- const headers = {
1767
- Authorization: `Bearer ${authToken}`,
1768
- 'Content-Type': method === 'POST'
1769
- ? 'application/json'
1770
- : 'application/x-www-form-urlencoded',
1771
- };
1772
- let fetchConfig;
1773
- if (method === 'GET') {
1774
- const searchParams = new URLSearchParams();
1775
- for (const [key, value] of Object.entries(params)) {
1776
- if (value !== undefined && value !== null) {
1777
- searchParams.append(key, String(value));
1778
- }
1779
- }
1780
- fetchConfig = {
1781
- method: 'GET',
1782
- headers,
1783
- };
1784
- const urlWithParams = `${url}?${searchParams.toString()}`;
1785
- const response = await fetch(urlWithParams, fetchConfig);
1786
- const data = (await response.json());
1787
- if (!response.ok && !data.ok) {
1788
- throw new Error(`Slack API error: ${data.error || 'Unknown error'}`);
1789
- }
1790
- return data;
1791
- }
1792
- else {
1793
- // Most Slack POST endpoints expect form-encoded data, not JSON
1794
- // Only specific endpoints like chat.postMessage with blocks expect JSON
1795
- const needsJson = ['chat.postMessage', 'chat.update'].includes(endpoint) &&
1796
- (params.blocks || params.attachments);
1797
- if (needsJson) {
1798
- fetchConfig = {
1799
- method: 'POST',
1800
- headers,
1801
- body: JSON.stringify(params),
1802
- };
1803
- }
1804
- else {
1805
- // Use form-encoded for most endpoints
1806
- const formData = new URLSearchParams();
1807
- for (const [key, value] of Object.entries(params)) {
1808
- if (value !== undefined && value !== null) {
1809
- formData.append(key, String(value));
1810
- }
1811
- }
1812
- fetchConfig = {
1813
- method: 'POST',
1814
- headers: {
1815
- ...headers,
1816
- 'Content-Type': 'application/x-www-form-urlencoded',
1817
- },
1818
- body: formData.toString(),
1819
- };
1820
- }
1821
- const response = await fetch(url, fetchConfig);
1822
- const data = (await response.json());
1823
- if (!response.ok && !data.ok) {
1824
- throw new Error(`Slack API error: ${data.error || 'Unknown error'}`);
1825
- }
1826
- return data;
1827
- }
1828
- }
1829
- }
1
+ // Re-export everything from the slack folder for backwards compatibility
2
+ export * from './slack/index.js';
1830
3
  //# sourceMappingURL=slack.js.map