opencode-skills-collection 1.0.185 → 1.0.187

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 (71) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +5 -1
  2. package/bundled-skills/3d-web-experience/SKILL.md +152 -37
  3. package/bundled-skills/agent-evaluation/SKILL.md +1088 -26
  4. package/bundled-skills/agent-memory-systems/SKILL.md +1037 -25
  5. package/bundled-skills/agent-tool-builder/SKILL.md +668 -16
  6. package/bundled-skills/ai-agents-architect/SKILL.md +271 -31
  7. package/bundled-skills/ai-product/SKILL.md +716 -26
  8. package/bundled-skills/ai-wrapper-product/SKILL.md +450 -44
  9. package/bundled-skills/algolia-search/SKILL.md +867 -15
  10. package/bundled-skills/autonomous-agents/SKILL.md +1033 -26
  11. package/bundled-skills/aws-serverless/SKILL.md +1046 -35
  12. package/bundled-skills/azure-functions/SKILL.md +1318 -19
  13. package/bundled-skills/browser-automation/SKILL.md +1065 -28
  14. package/bundled-skills/browser-extension-builder/SKILL.md +159 -32
  15. package/bundled-skills/bullmq-specialist/SKILL.md +347 -16
  16. package/bundled-skills/clerk-auth/SKILL.md +796 -15
  17. package/bundled-skills/computer-use-agents/SKILL.md +1870 -28
  18. package/bundled-skills/context-window-management/SKILL.md +271 -18
  19. package/bundled-skills/conversation-memory/SKILL.md +453 -24
  20. package/bundled-skills/crewai/SKILL.md +252 -46
  21. package/bundled-skills/discord-bot-architect/SKILL.md +1207 -34
  22. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  23. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  24. package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
  25. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  26. package/bundled-skills/docs/users/bundles.md +1 -1
  27. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  28. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  29. package/bundled-skills/docs/users/getting-started.md +1 -1
  30. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  31. package/bundled-skills/docs/users/usage.md +4 -4
  32. package/bundled-skills/docs/users/visual-guide.md +4 -4
  33. package/bundled-skills/email-systems/SKILL.md +646 -26
  34. package/bundled-skills/faf-expert/SKILL.md +221 -0
  35. package/bundled-skills/faf-wizard/SKILL.md +252 -0
  36. package/bundled-skills/file-uploads/SKILL.md +212 -11
  37. package/bundled-skills/firebase/SKILL.md +646 -16
  38. package/bundled-skills/gcp-cloud-run/SKILL.md +1117 -32
  39. package/bundled-skills/graphql/SKILL.md +1026 -27
  40. package/bundled-skills/hubspot-integration/SKILL.md +804 -19
  41. package/bundled-skills/idea-darwin/SKILL.md +120 -0
  42. package/bundled-skills/inngest/SKILL.md +431 -16
  43. package/bundled-skills/interactive-portfolio/SKILL.md +342 -44
  44. package/bundled-skills/langfuse/SKILL.md +296 -41
  45. package/bundled-skills/langgraph/SKILL.md +259 -50
  46. package/bundled-skills/micro-saas-launcher/SKILL.md +343 -44
  47. package/bundled-skills/neon-postgres/SKILL.md +572 -15
  48. package/bundled-skills/nextjs-supabase-auth/SKILL.md +269 -21
  49. package/bundled-skills/notion-template-business/SKILL.md +371 -44
  50. package/bundled-skills/personal-tool-builder/SKILL.md +537 -44
  51. package/bundled-skills/plaid-fintech/SKILL.md +825 -19
  52. package/bundled-skills/prompt-caching/SKILL.md +438 -25
  53. package/bundled-skills/rag-engineer/SKILL.md +271 -29
  54. package/bundled-skills/salesforce-development/SKILL.md +912 -19
  55. package/bundled-skills/satori/SKILL.md +54 -0
  56. package/bundled-skills/scroll-experience/SKILL.md +381 -44
  57. package/bundled-skills/segment-cdp/SKILL.md +817 -19
  58. package/bundled-skills/shopify-apps/SKILL.md +1475 -19
  59. package/bundled-skills/slack-bot-builder/SKILL.md +1162 -28
  60. package/bundled-skills/telegram-bot-builder/SKILL.md +152 -37
  61. package/bundled-skills/telegram-mini-app/SKILL.md +445 -44
  62. package/bundled-skills/trigger-dev/SKILL.md +916 -27
  63. package/bundled-skills/twilio-communications/SKILL.md +1310 -28
  64. package/bundled-skills/upstash-qstash/SKILL.md +898 -27
  65. package/bundled-skills/vercel-deployment/SKILL.md +637 -39
  66. package/bundled-skills/viral-generator-builder/SKILL.md +132 -37
  67. package/bundled-skills/voice-agents/SKILL.md +937 -27
  68. package/bundled-skills/voice-ai-development/SKILL.md +375 -46
  69. package/bundled-skills/workflow-automation/SKILL.md +982 -29
  70. package/bundled-skills/zapier-make-patterns/SKILL.md +772 -27
  71. package/package.json +1 -1
@@ -1,13 +1,21 @@
1
1
  ---
2
2
  name: slack-bot-builder
3
- description: "The Bolt framework is Slack's recommended approach for building apps. It handles authentication, event routing, request verification, and HTTP request processing so you can focus on app logic."
3
+ description: Build Slack apps using the Bolt framework across Python,
4
+ JavaScript, and Java. Covers Block Kit for rich UIs, interactive components,
5
+ slash commands, event handling, OAuth installation flows, and Workflow Builder
6
+ integration.
4
7
  risk: unknown
5
- source: "vibeship-spawner-skills (Apache 2.0)"
6
- date_added: "2026-02-27"
8
+ source: vibeship-spawner-skills (Apache 2.0)
9
+ date_added: 2026-02-27
7
10
  ---
8
11
 
9
12
  # Slack Bot Builder
10
13
 
14
+ Build Slack apps using the Bolt framework across Python, JavaScript, and Java.
15
+ Covers Block Kit for rich UIs, interactive components, slash commands,
16
+ event handling, OAuth installation flows, and Workflow Builder integration.
17
+ Focus on best practices for production-ready Slack apps.
18
+
11
19
  ## Patterns
12
20
 
13
21
  ### Bolt App Foundation Pattern
@@ -24,10 +32,8 @@ Key benefits:
24
32
 
25
33
  Available in: Python, JavaScript (Node.js), Java
26
34
 
35
+ **When to use**: Starting any new Slack app,Migrating from legacy Slack APIs,Building production Slack integrations
27
36
 
28
- **When to use**: ['Starting any new Slack app', 'Migrating from legacy Slack APIs', 'Building production Slack integrations']
29
-
30
- ```python
31
37
  # Python Bolt App
32
38
  from slack_bolt import App
33
39
  from slack_bolt.adapter.socket_mode import SocketModeHandler
@@ -87,8 +93,111 @@ def handle_ticket_command(ack, body, client):
87
93
  "element": {
88
94
  "type": "static_select",
89
95
  "action_id": "priority_select",
90
-
91
- ```
96
+ "options": [
97
+ {"text": {"type": "plain_text", "text": "Low"}, "value": "low"},
98
+ {"text": {"type": "plain_text", "text": "Medium"}, "value": "medium"},
99
+ {"text": {"type": "plain_text", "text": "High"}, "value": "high"}
100
+ ]
101
+ },
102
+ "label": {"type": "plain_text", "text": "Priority"}
103
+ }
104
+ ]
105
+ }
106
+ )
107
+
108
+ # Handle modal submission
109
+ @app.view("ticket_modal")
110
+ def handle_ticket_submission(ack, body, client, view):
111
+ """Handle ticket modal submission."""
112
+ ack()
113
+
114
+ # Extract values from the view
115
+ values = view["state"]["values"]
116
+ title = values["title_block"]["title_input"]["value"]
117
+ desc = values["desc_block"]["desc_input"]["value"]
118
+ priority = values["priority_block"]["priority_select"]["selected_option"]["value"]
119
+ user_id = body["user"]["id"]
120
+
121
+ # Create ticket in your system
122
+ ticket_id = create_ticket(title, desc, priority, user_id)
123
+
124
+ # Notify user
125
+ client.chat_postMessage(
126
+ channel=user_id,
127
+ text=f"Ticket #{ticket_id} created: {title}"
128
+ )
129
+
130
+ # Handle button clicks
131
+ @app.action("approve_button")
132
+ def handle_approval(ack, body, client):
133
+ """Handle approval button click."""
134
+ ack()
135
+
136
+ # Get context from the action
137
+ user = body["user"]["id"]
138
+ action_value = body["actions"][0]["value"]
139
+
140
+ # Update the message to remove interactive elements
141
+ # (Best practice: prevent double-clicks)
142
+ client.chat_update(
143
+ channel=body["channel"]["id"],
144
+ ts=body["message"]["ts"],
145
+ text=f"Approved by <@{user}>",
146
+ blocks=[] # Remove interactive blocks
147
+ )
148
+
149
+ # Listen for app_home_opened events
150
+ @app.event("app_home_opened")
151
+ def update_home_tab(client, event):
152
+ """Update the Home tab when user opens it."""
153
+ client.views_publish(
154
+ user_id=event["user"],
155
+ view={
156
+ "type": "home",
157
+ "blocks": [
158
+ {
159
+ "type": "section",
160
+ "text": {
161
+ "type": "mrkdwn",
162
+ "text": "*Welcome to the Ticket Bot!*"
163
+ }
164
+ },
165
+ {
166
+ "type": "actions",
167
+ "elements": [
168
+ {
169
+ "type": "button",
170
+ "text": {"type": "plain_text", "text": "Create Ticket"},
171
+ "action_id": "create_ticket_button"
172
+ }
173
+ ]
174
+ }
175
+ ]
176
+ }
177
+ )
178
+
179
+ # Socket Mode for development (no public URL needed)
180
+ if __name__ == "__main__":
181
+ handler = SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"])
182
+ handler.start()
183
+
184
+ # For production, use HTTP mode with a web server
185
+ # from flask import Flask, request
186
+ # from slack_bolt.adapter.flask import SlackRequestHandler
187
+ #
188
+ # flask_app = Flask(__name__)
189
+ # handler = SlackRequestHandler(app)
190
+ #
191
+ # @flask_app.route("/slack/events", methods=["POST"])
192
+ # def slack_events():
193
+ # return handler.handle(request)
194
+
195
+ ### Anti_patterns
196
+
197
+ - Not acknowledging requests within 3 seconds
198
+ - Blocking operations in the ack handler
199
+ - Hardcoding tokens in source code
200
+ - Not using Socket Mode for development
92
201
 
93
202
  ### Block Kit UI Pattern
94
203
 
@@ -103,10 +212,8 @@ Limits:
103
212
 
104
213
  Use Block Kit Builder to prototype: https://app.slack.com/block-kit-builder
105
214
 
215
+ **When to use**: Building rich message layouts,Adding interactive components to messages,Creating forms in modals,Building Home tab experiences
106
216
 
107
- **When to use**: ['Building rich message layouts', 'Adding interactive components to messages', 'Creating forms in modals', 'Building Home tab experiences']
108
-
109
- ```python
110
217
  from slack_bolt import App
111
218
  import os
112
219
 
@@ -171,8 +278,133 @@ def build_notification_blocks(incident: dict) -> list:
171
278
  "type": "button",
172
279
  "text": {"type": "plain_text", "text": "Acknowledge"},
173
280
  "style": "primary",
174
- "action_id": "acknowle
175
- ```
281
+ "action_id": "acknowledge_incident",
282
+ "value": incident['id']
283
+ },
284
+ {
285
+ "type": "button",
286
+ "text": {"type": "plain_text", "text": "Resolve"},
287
+ "style": "danger",
288
+ "action_id": "resolve_incident",
289
+ "value": incident['id'],
290
+ "confirm": {
291
+ "title": {"type": "plain_text", "text": "Resolve Incident?"},
292
+ "text": {"type": "mrkdwn", "text": "Are you sure this incident is resolved?"},
293
+ "confirm": {"type": "plain_text", "text": "Yes, Resolve"},
294
+ "deny": {"type": "plain_text", "text": "Cancel"}
295
+ }
296
+ },
297
+ {
298
+ "type": "button",
299
+ "text": {"type": "plain_text", "text": "View Details"},
300
+ "action_id": "view_incident",
301
+ "value": incident['id'],
302
+ "url": f"https://incidents.example.com/{incident['id']}"
303
+ }
304
+ ]
305
+ },
306
+ # Context footer
307
+ {
308
+ "type": "context",
309
+ "elements": [
310
+ {
311
+ "type": "mrkdwn",
312
+ "text": f"Incident ID: {incident['id']} | <https://runbook.example.com/{incident['service']}|View Runbook>"
313
+ }
314
+ ]
315
+ }
316
+ ]
317
+
318
+ def send_incident_notification(channel: str, incident: dict):
319
+ """Send incident notification with Block Kit."""
320
+ blocks = build_notification_blocks(incident)
321
+
322
+ app.client.chat_postMessage(
323
+ channel=channel,
324
+ text=f"Incident: {incident['title']}", # Fallback for notifications
325
+ blocks=blocks
326
+ )
327
+
328
+ # Handle button actions
329
+ @app.action("acknowledge_incident")
330
+ def handle_acknowledge(ack, body, client):
331
+ """Handle incident acknowledgment."""
332
+ ack()
333
+
334
+ incident_id = body["actions"][0]["value"]
335
+ user = body["user"]["id"]
336
+
337
+ # Update your system
338
+ acknowledge_incident(incident_id, user)
339
+
340
+ # Update message to show acknowledgment
341
+ original_blocks = body["message"]["blocks"]
342
+
343
+ # Add acknowledgment to context
344
+ original_blocks[-1]["elements"].append({
345
+ "type": "mrkdwn",
346
+ "text": f":white_check_mark: Acknowledged by <@{user}>"
347
+ })
348
+
349
+ # Remove acknowledge button (prevent double-click)
350
+ action_block = next(b for b in original_blocks if b.get("block_id", "").startswith("incident_actions"))
351
+ action_block["elements"] = [e for e in action_block["elements"] if e["action_id"] != "acknowledge_incident"]
352
+
353
+ client.chat_update(
354
+ channel=body["channel"]["id"],
355
+ ts=body["message"]["ts"],
356
+ blocks=original_blocks
357
+ )
358
+
359
+ # Interactive select menus
360
+ def build_user_selector_blocks():
361
+ """Build blocks with user selector."""
362
+ return [
363
+ {
364
+ "type": "section",
365
+ "text": {"type": "mrkdwn", "text": "Assign this task:"},
366
+ "accessory": {
367
+ "type": "users_select",
368
+ "action_id": "assign_user",
369
+ "placeholder": {"type": "plain_text", "text": "Select assignee"}
370
+ }
371
+ }
372
+ ]
373
+
374
+ # Overflow menu for more options
375
+ def build_task_blocks(task: dict):
376
+ """Build task blocks with overflow menu."""
377
+ return [
378
+ {
379
+ "type": "section",
380
+ "text": {"type": "mrkdwn", "text": f"*{task['title']}*"},
381
+ "accessory": {
382
+ "type": "overflow",
383
+ "action_id": "task_overflow",
384
+ "options": [
385
+ {
386
+ "text": {"type": "plain_text", "text": "Edit"},
387
+ "value": f"edit_{task['id']}"
388
+ },
389
+ {
390
+ "text": {"type": "plain_text", "text": "Delete"},
391
+ "value": f"delete_{task['id']}"
392
+ },
393
+ {
394
+ "text": {"type": "plain_text", "text": "Share"},
395
+ "value": f"share_{task['id']}"
396
+ }
397
+ ]
398
+ }
399
+ }
400
+ ]
401
+
402
+ ### Anti_patterns
403
+
404
+ - Exceeding 50 blocks per message
405
+ - Not providing fallback text for accessibility
406
+ - Hardcoding action_ids (use dynamic IDs when needed)
407
+ - Not handling button clicks idempotently
176
408
 
177
409
  ### OAuth Installation Pattern
178
410
 
@@ -189,10 +421,8 @@ Key OAuth concepts:
189
421
  70% of users abandon installation when confronted with excessive
190
422
  permission requests - request only what you need!
191
423
 
424
+ **When to use**: Distributing app to multiple workspaces,Building public Slack apps,Enterprise-grade integrations
192
425
 
193
- **When to use**: ['Distributing app to multiple workspaces', 'Building public Slack apps', 'Enterprise-grade integrations']
194
-
195
- ```python
196
426
  from slack_bolt import App
197
427
  from slack_bolt.oauth.oauth_settings import OAuthSettings
198
428
  from slack_sdk.oauth.installation_store import FileInstallationStore
@@ -250,20 +480,924 @@ app = App(
250
480
  )
251
481
  )
252
482
 
253
- # OAuth routes are handled a
483
+ # OAuth routes are handled automatically by Bolt
484
+ # /slack/install - Initiates OAuth flow
485
+ # /slack/oauth_redirect - Handles callback
486
+
487
+ # Flask integration
488
+ from flask import Flask, request
489
+ from slack_bolt.adapter.flask import SlackRequestHandler
490
+
491
+ flask_app = Flask(__name__)
492
+ handler = SlackRequestHandler(app)
493
+
494
+ @flask_app.route("/slack/install", methods=["GET"])
495
+ def install():
496
+ return handler.handle(request)
497
+
498
+ @flask_app.route("/slack/oauth_redirect", methods=["GET"])
499
+ def oauth_redirect():
500
+ return handler.handle(request)
501
+
502
+ @flask_app.route("/slack/events", methods=["POST"])
503
+ def slack_events():
504
+ return handler.handle(request)
505
+
506
+ # Handle installation success/failure
507
+ @app.oauth_success
508
+ def handle_oauth_success(args):
509
+ """Called when OAuth completes successfully."""
510
+ installation = args["installation"]
511
+
512
+ # Send welcome message
513
+ app.client.chat_postMessage(
514
+ token=installation.bot_token,
515
+ channel=installation.user_id,
516
+ text="Thanks for installing! Type /help to get started."
517
+ )
518
+
519
+ return "Installation successful! You can close this window."
520
+
521
+ @app.oauth_failure
522
+ def handle_oauth_failure(args):
523
+ """Called when OAuth fails."""
524
+ error = args.get("error", "Unknown error")
525
+ return f"Installation failed: {error}"
526
+
527
+ # Scope management - request additional scopes when needed
528
+ def request_additional_scopes(team_id: str, new_scopes: list):
529
+ """
530
+ Generate URL for user to add scopes.
531
+ Note: Existing tokens retain old scopes.
532
+ User must re-authorize for new scopes.
533
+ """
534
+ base_url = "https://slack.com/oauth/v2/authorize"
535
+ params = {
536
+ "client_id": os.environ["SLACK_CLIENT_ID"],
537
+ "scope": ",".join(new_scopes),
538
+ "team": team_id
539
+ }
540
+ return f"{base_url}?{urlencode(params)}"
541
+
542
+ ### Anti_patterns
543
+
544
+ - Requesting unnecessary scopes upfront
545
+ - Storing tokens in plain text
546
+ - Not validating OAuth state parameter (CSRF risk)
547
+ - Assuming tokens have new scopes after config change
548
+
549
+ ### Socket Mode Pattern
550
+
551
+ Socket Mode allows your app to receive events via WebSocket instead
552
+ of public HTTP endpoints. Perfect for development and apps behind
553
+ firewalls.
554
+
555
+ Benefits:
556
+ - No public URL needed
557
+ - Works behind corporate firewalls
558
+ - Simpler local development
559
+ - Real-time bidirectional communication
560
+
561
+ Limitation: Not recommended for high-volume production apps.
562
+
563
+ **When to use**: Local development,Apps behind corporate firewalls,Internal tools with security constraints,Prototyping and testing
564
+
565
+ from slack_bolt import App
566
+ from slack_bolt.adapter.socket_mode import SocketModeHandler
567
+ import os
568
+
569
+ # Socket Mode requires an app-level token (xapp-...)
570
+ # Create in App Settings > Basic Information > App-Level Tokens
571
+ # Needs 'connections:write' scope
572
+
573
+ app = App(token=os.environ["SLACK_BOT_TOKEN"])
574
+
575
+ @app.message("hello")
576
+ def handle_hello(message, say):
577
+ say(f"Hey <@{message['user']}>!")
578
+
579
+ @app.command("/status")
580
+ def handle_status(ack, say):
581
+ ack()
582
+ say("All systems operational!")
583
+
584
+ @app.event("app_mention")
585
+ def handle_mention(event, say):
586
+ say(f"You mentioned me, <@{event['user']}>!")
587
+
588
+ if __name__ == "__main__":
589
+ # SocketModeHandler manages the WebSocket connection
590
+ handler = SocketModeHandler(
591
+ app,
592
+ os.environ["SLACK_APP_TOKEN"] # xapp-... token
593
+ )
594
+
595
+ print("Starting Socket Mode...")
596
+ handler.start()
597
+
598
+ # For async apps
599
+ from slack_bolt.async_app import AsyncApp
600
+ from slack_bolt.adapter.socket_mode.async_handler import AsyncSocketModeHandler
601
+ import asyncio
602
+
603
+ async_app = AsyncApp(token=os.environ["SLACK_BOT_TOKEN"])
604
+
605
+ @async_app.message("hello")
606
+ async def handle_hello_async(message, say):
607
+ await say(f"Hey <@{message['user']}>!")
608
+
609
+ async def main():
610
+ handler = AsyncSocketModeHandler(async_app, os.environ["SLACK_APP_TOKEN"])
611
+ await handler.start_async()
612
+
613
+ if __name__ == "__main__":
614
+ asyncio.run(main())
615
+
616
+ ### Anti_patterns
617
+
618
+ - Using Socket Mode for high-volume production apps
619
+ - Not handling WebSocket disconnections
620
+ - Forgetting to create app-level token
621
+ - Using bot token instead of app token
622
+
623
+ ### Workflow Builder Step Pattern
624
+
625
+ Extend Slack's Workflow Builder with custom steps powered by your app.
626
+ Users can include your custom steps in their no-code workflows.
627
+
628
+ Workflow steps can:
629
+ - Collect input from users
630
+ - Execute custom logic
631
+ - Output data for subsequent steps
632
+
633
+ **When to use**: Integrating with Workflow Builder,Enabling non-technical users to use your features,Building reusable automation components
634
+
635
+ from slack_bolt import App
636
+ from slack_bolt.workflows.step import WorkflowStep
637
+ import os
638
+
639
+ app = App(
640
+ token=os.environ["SLACK_BOT_TOKEN"],
641
+ signing_secret=os.environ["SLACK_SIGNING_SECRET"]
642
+ )
643
+
644
+ # Define the workflow step
645
+ def edit(ack, step, configure):
646
+ """Called when user adds/edits the step in Workflow Builder."""
647
+ ack()
648
+
649
+ # Show configuration modal
650
+ blocks = [
651
+ {
652
+ "type": "input",
653
+ "block_id": "ticket_type",
654
+ "element": {
655
+ "type": "static_select",
656
+ "action_id": "type_select",
657
+ "options": [
658
+ {"text": {"type": "plain_text", "text": "Bug"}, "value": "bug"},
659
+ {"text": {"type": "plain_text", "text": "Feature"}, "value": "feature"},
660
+ {"text": {"type": "plain_text", "text": "Task"}, "value": "task"}
661
+ ]
662
+ },
663
+ "label": {"type": "plain_text", "text": "Ticket Type"}
664
+ },
665
+ {
666
+ "type": "input",
667
+ "block_id": "title_input",
668
+ "element": {
669
+ "type": "plain_text_input",
670
+ "action_id": "title"
671
+ },
672
+ "label": {"type": "plain_text", "text": "Title"}
673
+ },
674
+ {
675
+ "type": "input",
676
+ "block_id": "assignee_input",
677
+ "element": {
678
+ "type": "users_select",
679
+ "action_id": "assignee"
680
+ },
681
+ "label": {"type": "plain_text", "text": "Assignee"}
682
+ }
683
+ ]
684
+
685
+ configure(blocks=blocks)
686
+
687
+ def save(ack, view, update):
688
+ """Called when user saves step configuration."""
689
+ ack()
690
+
691
+ values = view["state"]["values"]
692
+
693
+ # Define inputs (from user's configuration)
694
+ inputs = {
695
+ "ticket_type": {
696
+ "value": values["ticket_type"]["type_select"]["selected_option"]["value"]
697
+ },
698
+ "title": {
699
+ "value": values["title_input"]["title"]["value"]
700
+ },
701
+ "assignee": {
702
+ "value": values["assignee_input"]["assignee"]["selected_user"]
703
+ }
704
+ }
705
+
706
+ # Define outputs (available to subsequent steps)
707
+ outputs = [
708
+ {
709
+ "name": "ticket_id",
710
+ "type": "text",
711
+ "label": "Created Ticket ID"
712
+ },
713
+ {
714
+ "name": "ticket_url",
715
+ "type": "text",
716
+ "label": "Ticket URL"
717
+ }
718
+ ]
719
+
720
+ update(inputs=inputs, outputs=outputs)
721
+
722
+ def execute(step, complete, fail):
723
+ """Called when the step runs in a workflow."""
724
+ inputs = step["inputs"]
725
+
726
+ try:
727
+ # Get input values
728
+ ticket_type = inputs["ticket_type"]["value"]
729
+ title = inputs["title"]["value"]
730
+ assignee = inputs["assignee"]["value"]
731
+
732
+ # Create ticket in your system
733
+ ticket = create_ticket(
734
+ type=ticket_type,
735
+ title=title,
736
+ assignee=assignee
737
+ )
738
+
739
+ # Complete with outputs
740
+ complete(outputs={
741
+ "ticket_id": ticket["id"],
742
+ "ticket_url": ticket["url"]
743
+ })
744
+
745
+ except Exception as e:
746
+ fail(error={"message": str(e)})
747
+
748
+ # Register the workflow step
749
+ create_ticket_step = WorkflowStep(
750
+ callback_id="create_ticket_step",
751
+ edit=edit,
752
+ save=save,
753
+ execute=execute
754
+ )
755
+
756
+ app.step(create_ticket_step)
757
+
758
+ ### Anti_patterns
759
+
760
+ - Not calling complete() or fail() in execute
761
+ - Long-running operations without progress updates
762
+ - Not validating inputs in execute
763
+ - Exposing sensitive data in outputs
764
+
765
+ ## Sharp Edges
766
+
767
+ ### Missing 3-Second Acknowledgment (Timeout)
768
+
769
+ Severity: CRITICAL
770
+
771
+ Situation: Handling slash commands, shortcuts, or interactive components
772
+
773
+ Symptoms:
774
+ User sees "This command timed out" or "Something went wrong."
775
+ The action never completes even though your code runs.
776
+ Works in development but fails in production.
777
+
778
+ Why this breaks:
779
+ Slack requires acknowledgment within 3 seconds for ALL interactive requests:
780
+ - Slash commands
781
+ - Button/select menu clicks
782
+ - Modal submissions
783
+ - Shortcuts
784
+
785
+ If you do ANY slow operation (database, API call, LLM) before responding,
786
+ you'll miss the window. Slack shows an error even if your bot eventually
787
+ processes the request correctly.
788
+
789
+ Recommended fix:
790
+
791
+ ## Acknowledge immediately, process later
792
+
793
+ ```python
794
+ from slack_bolt import App
795
+ from slack_bolt.adapter.socket_mode import SocketModeHandler
796
+ import threading
797
+
798
+ app = App(token=os.environ["SLACK_BOT_TOKEN"])
799
+
800
+ @app.command("/slow-task")
801
+ def handle_slow_task(ack, command, client, respond):
802
+ # ACK IMMEDIATELY - before any processing
803
+ ack("Processing your request...")
804
+
805
+ # Do slow work in background
806
+ def do_work():
807
+ result = call_slow_api(command["text"]) # Takes 10 seconds
808
+ respond(f"Done! Result: {result}")
809
+
810
+ threading.Thread(target=do_work).start()
811
+
812
+ @app.view("modal_submission")
813
+ def handle_modal(ack, body, client, view):
814
+ # ACK with response_action for modals
815
+ ack(response_action="clear") # Or "update" with new view
816
+
817
+ # Process in background
818
+ user_id = body["user"]["id"]
819
+ values = view["state"]["values"]
820
+ # ... slow processing
254
821
  ```
255
822
 
256
- ## ⚠️ Sharp Edges
823
+ ## For Bolt framework - use lazy listeners
824
+
825
+ ```python
826
+ # Bolt handles ack() automatically with lazy listeners
827
+ @app.command("/slow-task")
828
+ def handle_slow_task(ack, command, respond):
829
+ ack() # Still call ack() first!
830
+
831
+ @handle_slow_task.lazy
832
+ def process_slow_task(command, respond):
833
+ # This runs after ack, can take as long as needed
834
+ result = slow_operation(command["text"])
835
+ respond(result)
836
+ ```
837
+
838
+ ### Not Validating OAuth State Parameter (CSRF)
839
+
840
+ Severity: CRITICAL
841
+
842
+ Situation: Implementing OAuth installation flow
257
843
 
258
- | Issue | Severity | Solution |
259
- |-------|----------|----------|
260
- | Issue | critical | ## Acknowledge immediately, process later |
261
- | Issue | critical | ## Proper state validation |
262
- | Issue | critical | ## Never hardcode or log tokens |
263
- | Issue | high | ## Request minimum required scopes |
264
- | Issue | medium | ## Know and respect the limits |
265
- | Issue | high | ## Socket Mode: Only for development |
266
- | Issue | critical | ## Bolt handles this automatically |
844
+ Symptoms:
845
+ Bot appears to work, but you're vulnerable to CSRF attacks.
846
+ Attackers could trick users into installing malicious configurations.
847
+
848
+ Why this breaks:
849
+ The OAuth state parameter prevents CSRF attacks. Flow:
850
+ 1. You generate random state, store it, send to Slack
851
+ 2. User authorizes in Slack
852
+ 3. Slack redirects back with code + state
853
+ 4. You MUST verify state matches what you stored
854
+
855
+ Without this, an attacker can craft a malicious OAuth URL and trick
856
+ admins into completing the flow with attacker's authorization code.
857
+
858
+ Recommended fix:
859
+
860
+ ## Proper state validation
861
+
862
+ ```python
863
+ import secrets
864
+ from flask import Flask, request, session, redirect
865
+ from slack_sdk.oauth import AuthorizeUrlGenerator
866
+ from slack_sdk.oauth.state_store import FileOAuthStateStore
867
+
868
+ app = Flask(__name__)
869
+ app.secret_key = os.environ["SESSION_SECRET"]
870
+
871
+ # Use Slack SDK's state store (Redis recommended for production)
872
+ state_store = FileOAuthStateStore(
873
+ expiration_seconds=300, # 5 minutes
874
+ base_dir="./oauth_states"
875
+ )
876
+
877
+ @app.route("/slack/install")
878
+ def install():
879
+ # Generate cryptographically secure state
880
+ state = state_store.issue()
881
+
882
+ # Store in session for verification
883
+ session["oauth_state"] = state
884
+
885
+ authorize_url = AuthorizeUrlGenerator(
886
+ client_id=os.environ["SLACK_CLIENT_ID"],
887
+ scopes=["channels:history", "chat:write"],
888
+ user_scopes=[]
889
+ ).generate(state)
890
+
891
+ return redirect(authorize_url)
892
+
893
+ @app.route("/slack/oauth/callback")
894
+ def oauth_callback():
895
+ # CRITICAL: Verify state
896
+ received_state = request.args.get("state")
897
+ stored_state = session.get("oauth_state")
898
+
899
+ if not received_state or received_state != stored_state:
900
+ return "Invalid state parameter - possible CSRF attack", 403
901
+
902
+ # Also use state_store.consume() for one-time use
903
+ if not state_store.consume(received_state):
904
+ return "State already used or expired", 403
905
+
906
+ # Now safe to exchange code for token
907
+ code = request.args.get("code")
908
+ # ... complete OAuth flow
909
+ ```
910
+
911
+ ### Exposing Bot/User Tokens
912
+
913
+ Severity: CRITICAL
914
+
915
+ Situation: Storing or logging Slack tokens
916
+
917
+ Symptoms:
918
+ Unauthorized messages sent from your bot. Attackers reading private
919
+ channels. Token found in logs, git history, or client-side code.
920
+
921
+ Why this breaks:
922
+ Slack tokens provide FULL access to whatever scopes they have:
923
+ - Bot tokens (xoxb-*): Access workspaces where installed
924
+ - User tokens (xoxp-*): Access as that specific user
925
+ - App-level tokens (xapp-*): Socket Mode connections
926
+
927
+ Common exposure points:
928
+ - Hardcoded in source code
929
+ - Logged in error messages
930
+ - Sent to frontend/client
931
+ - Stored in database without encryption
932
+
933
+ Recommended fix:
934
+
935
+ ## Never hardcode or log tokens
936
+
937
+ ```python
938
+ # BAD - never do this
939
+ client = WebClient(token="xoxb-12345-...")
940
+
941
+ # GOOD - environment variables
942
+ client = WebClient(token=os.environ["SLACK_BOT_TOKEN"])
943
+
944
+ # BAD - logging tokens
945
+ logger.error(f"API call failed with token {token}")
946
+
947
+ # GOOD - never log tokens
948
+ logger.error(f"API call failed for team {team_id}")
949
+
950
+ # BAD - sending token to frontend
951
+ return {"token": bot_token}
952
+
953
+ # GOOD - only send what frontend needs
954
+ return {"channels": channel_list}
955
+ ```
956
+
957
+ ## Encrypt tokens in database
958
+
959
+ ```python
960
+ from cryptography.fernet import Fernet
961
+
962
+ class TokenStore:
963
+ def __init__(self, encryption_key: str):
964
+ self.cipher = Fernet(encryption_key)
965
+
966
+ def save_token(self, team_id: str, token: str):
967
+ encrypted = self.cipher.encrypt(token.encode())
968
+ db.execute(
969
+ "INSERT INTO installations (team_id, encrypted_token) VALUES (?, ?)",
970
+ (team_id, encrypted)
971
+ )
972
+
973
+ def get_token(self, team_id: str) -> str:
974
+ row = db.execute(
975
+ "SELECT encrypted_token FROM installations WHERE team_id = ?",
976
+ (team_id,)
977
+ ).fetchone()
978
+ return self.cipher.decrypt(row[0]).decode()
979
+ ```
980
+
981
+ ## Rotate tokens if exposed
982
+
983
+ ```
984
+ 1. Slack API > Your App > OAuth & Permissions
985
+ 2. Click "Rotate" for the exposed token
986
+ 3. Update all deployments immediately
987
+ 4. Review Slack audit logs for unauthorized access
988
+ ```
989
+
990
+ ### Requesting Unnecessary OAuth Scopes
991
+
992
+ Severity: HIGH
993
+
994
+ Situation: Configuring OAuth scopes for your app
995
+
996
+ Symptoms:
997
+ Users hesitate to install due to scary permission warnings.
998
+ Lower install rates. Security team blocks deployment.
999
+ App rejected from Slack App Directory.
1000
+
1001
+ Why this breaks:
1002
+ Each OAuth scope grants specific permissions. Requesting more than
1003
+ you need:
1004
+ - Makes install consent screen scary
1005
+ - Increases attack surface if token leaked
1006
+ - May violate enterprise security policies
1007
+ - Can get your app rejected from App Directory
1008
+
1009
+ Common over-requests:
1010
+ - `admin` when you just need `chat:write`
1011
+ - `channels:read` when you only message one channel
1012
+ - `users:read.email` when you don't need emails
1013
+
1014
+ Recommended fix:
1015
+
1016
+ ## Request minimum required scopes
1017
+
1018
+ ```python
1019
+ # For a simple notification bot
1020
+ MINIMAL_SCOPES = [
1021
+ "chat:write", # Post messages
1022
+ "channels:join", # Join public channels (if needed)
1023
+ ]
1024
+
1025
+ # NOT NEEDED for basic notification:
1026
+ # - channels:read (unless you list channels)
1027
+ # - users:read (unless you look up users)
1028
+ # - channels:history (unless you read messages)
1029
+
1030
+ # For a slash command bot
1031
+ SLASH_COMMAND_SCOPES = [
1032
+ "commands", # Register slash commands
1033
+ "chat:write", # Respond to commands
1034
+ ]
1035
+
1036
+ # For a bot that responds to mentions
1037
+ MENTION_BOT_SCOPES = [
1038
+ "app_mentions:read", # Receive @mentions
1039
+ "chat:write", # Reply to mentions
1040
+ ]
1041
+ ```
1042
+
1043
+ ## Scope reference by use case
1044
+
1045
+ | Use Case | Required Scopes |
1046
+ |----------|-----------------|
1047
+ | Post messages | `chat:write` |
1048
+ | Slash commands | `commands` |
1049
+ | Respond to @mentions | `app_mentions:read`, `chat:write` |
1050
+ | Read channel messages | `channels:history` (public), `groups:history` (private) |
1051
+ | Read user info | `users:read` |
1052
+ | Open modals | `commands` or trigger from event |
1053
+ | Add reactions | `reactions:write` |
1054
+ | Upload files | `files:write` |
1055
+
1056
+ ## Progressive scope requests
1057
+
1058
+ ```python
1059
+ # Start with minimal scopes
1060
+ INITIAL_SCOPES = ["chat:write", "commands"]
1061
+
1062
+ # Request additional scopes only when needed
1063
+ @app.command("/enable-reactions")
1064
+ def enable_reactions(ack, client, command):
1065
+ ack()
1066
+
1067
+ # Check if we have the scope
1068
+ auth_result = client.auth_test()
1069
+ # If missing reactions:write, prompt re-auth
1070
+ if needs_additional_scope:
1071
+ # Send user to re-auth with additional scope
1072
+ pass
1073
+ ```
1074
+
1075
+ ### Exceeding Block Kit Limits
1076
+
1077
+ Severity: MEDIUM
1078
+
1079
+ Situation: Building complex message UIs with Block Kit
1080
+
1081
+ Symptoms:
1082
+ Message fails to send with "invalid_blocks" error.
1083
+ Modal won't open. Message truncated unexpectedly.
1084
+
1085
+ Why this breaks:
1086
+ Block Kit has strict limits that aren't always obvious:
1087
+ - 50 blocks per message/modal
1088
+ - 3000 characters per text block
1089
+ - 10 elements per actions block
1090
+ - 100 options per select menu
1091
+ - Modal: 50 blocks, 24KB total
1092
+ - Home tab: 100 blocks
1093
+
1094
+ Exceeding these causes silent failures or cryptic errors.
1095
+
1096
+ Recommended fix:
1097
+
1098
+ ## Know and respect the limits
1099
+
1100
+ ```python
1101
+ # Constants for Block Kit limits
1102
+ BLOCK_KIT_LIMITS = {
1103
+ "blocks_per_message": 50,
1104
+ "blocks_per_modal": 50,
1105
+ "blocks_per_home": 100,
1106
+ "text_block_chars": 3000,
1107
+ "elements_per_actions": 10,
1108
+ "options_per_select": 100,
1109
+ "modal_total_bytes": 24 * 1024, # 24KB
1110
+ }
1111
+
1112
+ def validate_blocks(blocks: list) -> tuple[bool, str]:
1113
+ """Validate blocks before sending."""
1114
+ if len(blocks) > BLOCK_KIT_LIMITS["blocks_per_message"]:
1115
+ return False, f"Too many blocks: {len(blocks)} > 50"
1116
+
1117
+ for block in blocks:
1118
+ if block.get("type") == "section":
1119
+ text = block.get("text", {}).get("text", "")
1120
+ if len(text) > BLOCK_KIT_LIMITS["text_block_chars"]:
1121
+ return False, f"Text too long: {len(text)} > 3000"
1122
+
1123
+ if block.get("type") == "actions":
1124
+ elements = block.get("elements", [])
1125
+ if len(elements) > BLOCK_KIT_LIMITS["elements_per_actions"]:
1126
+ return False, f"Too many actions: {len(elements)} > 10"
1127
+
1128
+ return True, "OK"
1129
+
1130
+ # Paginate long content
1131
+ def paginate_blocks(blocks: list, page: int = 0, per_page: int = 45):
1132
+ """Paginate blocks with navigation."""
1133
+ start = page * per_page
1134
+ end = start + per_page
1135
+ page_blocks = blocks[start:end]
1136
+
1137
+ # Add pagination controls
1138
+ if len(blocks) > per_page:
1139
+ page_blocks.append({
1140
+ "type": "actions",
1141
+ "elements": [
1142
+ {"type": "button", "text": {"type": "plain_text", "text": "Previous"},
1143
+ "action_id": f"page_{page-1}", "disabled": page == 0},
1144
+ {"type": "button", "text": {"type": "plain_text", "text": "Next"},
1145
+ "action_id": f"page_{page+1}",
1146
+ "disabled": end >= len(blocks)}
1147
+ ]
1148
+ })
1149
+
1150
+ return page_blocks
1151
+ ```
1152
+
1153
+ ### Using Socket Mode in Production
1154
+
1155
+ Severity: HIGH
1156
+
1157
+ Situation: Deploying Slack bot to production
1158
+
1159
+ Symptoms:
1160
+ Bot works in development but is unreliable in production.
1161
+ Missed events. Connection drops. Can't scale horizontally.
1162
+
1163
+ Why this breaks:
1164
+ Socket Mode is designed for development:
1165
+ - Single WebSocket connection per app
1166
+ - Can't scale to multiple instances
1167
+ - Connection can drop (needs reconnect logic)
1168
+ - No built-in load balancing
1169
+
1170
+ For production with multiple instances or high traffic,
1171
+ HTTP webhooks are more reliable.
1172
+
1173
+ Recommended fix:
1174
+
1175
+ ## Socket Mode: Only for development
1176
+
1177
+ ```python
1178
+ # Development with Socket Mode
1179
+ if os.environ.get("ENVIRONMENT") == "development":
1180
+ from slack_bolt.adapter.socket_mode import SocketModeHandler
1181
+ handler = SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"])
1182
+ handler.start()
1183
+ ```
1184
+
1185
+ ## Production: Use HTTP endpoints
1186
+
1187
+ ```python
1188
+ # Production with HTTP (Flask example)
1189
+ from slack_bolt.adapter.flask import SlackRequestHandler
1190
+ from flask import Flask, request
1191
+
1192
+ flask_app = Flask(__name__)
1193
+ handler = SlackRequestHandler(app)
1194
+
1195
+ @flask_app.route("/slack/events", methods=["POST"])
1196
+ def slack_events():
1197
+ return handler.handle(request)
1198
+
1199
+ @flask_app.route("/slack/commands", methods=["POST"])
1200
+ def slack_commands():
1201
+ return handler.handle(request)
1202
+
1203
+ @flask_app.route("/slack/interactions", methods=["POST"])
1204
+ def slack_interactions():
1205
+ return handler.handle(request)
1206
+ ```
1207
+
1208
+ ## If you must use Socket Mode in production
1209
+
1210
+ ```python
1211
+ from slack_bolt.adapter.socket_mode import SocketModeHandler
1212
+ import time
1213
+
1214
+ class RobustSocketHandler:
1215
+ def __init__(self, app, app_token):
1216
+ self.app = app
1217
+ self.app_token = app_token
1218
+ self.handler = None
1219
+
1220
+ def start(self):
1221
+ while True:
1222
+ try:
1223
+ self.handler = SocketModeHandler(self.app, self.app_token)
1224
+ self.handler.start()
1225
+ except Exception as e:
1226
+ logger.error(f"Socket Mode disconnected: {e}")
1227
+ time.sleep(5) # Backoff before reconnect
1228
+ ```
1229
+
1230
+ ### Not Verifying Request Signatures
1231
+
1232
+ Severity: CRITICAL
1233
+
1234
+ Situation: Receiving webhooks from Slack
1235
+
1236
+ Symptoms:
1237
+ Attackers can send fake requests to your webhook endpoints.
1238
+ Spoofed slash commands. Fake event notifications processed.
1239
+
1240
+ Why this breaks:
1241
+ Slack signs all requests with X-Slack-Signature header using your
1242
+ signing secret. Without verification, anyone who knows your webhook
1243
+ URL can send fake requests.
1244
+
1245
+ This is different from OAuth tokens - signing verifies the REQUEST
1246
+ came from Slack, not that you have permission to call Slack.
1247
+
1248
+ Recommended fix:
1249
+
1250
+ ## Bolt handles this automatically
1251
+
1252
+ ```python
1253
+ from slack_bolt import App
1254
+
1255
+ # Bolt verifies signatures automatically when you provide signing_secret
1256
+ app = App(
1257
+ token=os.environ["SLACK_BOT_TOKEN"],
1258
+ signing_secret=os.environ["SLACK_SIGNING_SECRET"]
1259
+ )
1260
+ # All requests to your handlers are verified
1261
+ ```
1262
+
1263
+ ## Manual verification (if not using Bolt)
1264
+
1265
+ ```python
1266
+ import hmac
1267
+ import hashlib
1268
+ import time
1269
+ from flask import Flask, request, abort
1270
+
1271
+ SIGNING_SECRET = os.environ["SLACK_SIGNING_SECRET"]
1272
+
1273
+ def verify_slack_signature(request):
1274
+ timestamp = request.headers.get("X-Slack-Request-Timestamp", "")
1275
+ signature = request.headers.get("X-Slack-Signature", "")
1276
+
1277
+ # Reject old timestamps (replay attack prevention)
1278
+ if abs(time.time() - int(timestamp)) > 60 * 5:
1279
+ return False
1280
+
1281
+ # Compute expected signature
1282
+ sig_basestring = f"v0:{timestamp}:{request.get_data(as_text=True)}"
1283
+ expected_sig = "v0=" + hmac.new(
1284
+ SIGNING_SECRET.encode(),
1285
+ sig_basestring.encode(),
1286
+ hashlib.sha256
1287
+ ).hexdigest()
1288
+
1289
+ # Constant-time comparison
1290
+ return hmac.compare_digest(expected_sig, signature)
1291
+
1292
+ @app.route("/slack/events", methods=["POST"])
1293
+ def slack_events():
1294
+ if not verify_slack_signature(request):
1295
+ abort(403)
1296
+ # Safe to process
1297
+ ```
1298
+
1299
+ ## Validation Checks
1300
+
1301
+ ### Hardcoded Slack Token
1302
+
1303
+ Severity: ERROR
1304
+
1305
+ Slack tokens must never be hardcoded
1306
+
1307
+ Message: Hardcoded Slack token detected. Use environment variables.
1308
+
1309
+ ### Signing Secret in Source Code
1310
+
1311
+ Severity: ERROR
1312
+
1313
+ Signing secrets should be in environment variables
1314
+
1315
+ Message: Hardcoded signing secret. Use os.environ['SLACK_SIGNING_SECRET'].
1316
+
1317
+ ### Webhook Without Signature Verification
1318
+
1319
+ Severity: ERROR
1320
+
1321
+ Slack webhooks must verify X-Slack-Signature
1322
+
1323
+ Message: Webhook without signature verification. Use Bolt or verify manually.
1324
+
1325
+ ### Slack Token in Client-Side Code
1326
+
1327
+ Severity: ERROR
1328
+
1329
+ Never expose Slack tokens to browsers
1330
+
1331
+ Message: Slack credentials exposed client-side. Only use server-side.
1332
+
1333
+ ### Slow Operation Before Acknowledgment
1334
+
1335
+ Severity: WARNING
1336
+
1337
+ ack() must be called before slow operations
1338
+
1339
+ Message: Slow operation before ack(). Call ack() first, then process.
1340
+
1341
+ ### Missing Acknowledgment Call
1342
+
1343
+ Severity: WARNING
1344
+
1345
+ Interactive handlers must call ack()
1346
+
1347
+ Message: Handler missing ack() call. Must acknowledge within 3 seconds.
1348
+
1349
+ ### OAuth Without State Validation
1350
+
1351
+ Severity: ERROR
1352
+
1353
+ OAuth callback must validate state parameter
1354
+
1355
+ Message: OAuth without state validation. Vulnerable to CSRF attacks.
1356
+
1357
+ ### Token Storage Without Encryption
1358
+
1359
+ Severity: WARNING
1360
+
1361
+ Tokens should be encrypted at rest
1362
+
1363
+ Message: Token stored without encryption. Encrypt tokens at rest.
1364
+
1365
+ ### Requesting Admin Scopes
1366
+
1367
+ Severity: WARNING
1368
+
1369
+ Avoid admin scopes unless absolutely necessary
1370
+
1371
+ Message: Requesting admin scope. Use minimal required scopes.
1372
+
1373
+ ### Potentially Unused Scope
1374
+
1375
+ Severity: INFO
1376
+
1377
+ Check if all requested scopes are used
1378
+
1379
+ Message: Requesting users:read.email but may not use email. Verify necessity.
1380
+
1381
+ ## Collaboration
1382
+
1383
+ ### Delegation Triggers
1384
+
1385
+ - user needs AI-powered Slack bot -> llm-architect (Integrate LLM for conversational Slack bot)
1386
+ - user needs voice notifications -> twilio-communications (Escalate Slack alerts to SMS or voice calls)
1387
+ - user needs workflow automation -> workflow-automation (Slack as trigger/action in n8n/Temporal workflows)
1388
+ - user needs bot for Discord too -> discord-bot-architect (Cross-platform bot architecture)
1389
+ - user needs full auth system -> auth-specialist (OAuth, workspace management, enterprise SSO)
1390
+ - user needs database for bot data -> postgres-wizard (Store installations, user preferences, message history)
1391
+ - user needs high availability -> devops (Scale webhooks, monitoring, alerting)
267
1392
 
268
1393
  ## When to Use
269
- This skill is applicable to execute the workflow or actions described in the overview.
1394
+
1395
+ - User mentions or implies: slack bot
1396
+ - User mentions or implies: slack app
1397
+ - User mentions or implies: bolt framework
1398
+ - User mentions or implies: block kit
1399
+ - User mentions or implies: slash command
1400
+ - User mentions or implies: slack webhook
1401
+ - User mentions or implies: slack workflow
1402
+ - User mentions or implies: slack interactive
1403
+ - User mentions or implies: slack oauth