chub-dev 0.1.0 → 0.1.2-beta.0
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.
- package/README.md +55 -0
- package/bin/chub-mcp +2 -0
- package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
- package/dist/airtable/docs/database/python/DOC.md +1735 -0
- package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
- package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
- package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
- package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
- package/dist/asana/docs/tasks/DOC.md +1396 -0
- package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
- package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
- package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
- package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
- package/dist/auth0/docs/identity/python/DOC.md +1199 -0
- package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
- package/dist/aws/docs/s3/python/DOC.md +1807 -0
- package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
- package/dist/binance/docs/trading/python/DOC.md +1454 -0
- package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
- package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
- package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
- package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
- package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
- package/dist/clerk/docs/auth/python/DOC.md +274 -0
- package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
- package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
- package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
- package/dist/cohere/docs/llm/DOC.md +1335 -0
- package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
- package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
- package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
- package/dist/deepgram/docs/speech/python/DOC.md +685 -0
- package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
- package/dist/deepl/docs/translation/python/DOC.md +944 -0
- package/dist/deepseek/docs/llm/DOC.md +1220 -0
- package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
- package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
- package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
- package/dist/discord/docs/bot/python/DOC.md +1130 -0
- package/dist/elasticsearch/docs/search/DOC.md +1634 -0
- package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
- package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
- package/dist/firebase/docs/auth/DOC.md +1015 -0
- package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
- package/dist/gemini/docs/genai/python/DOC.md +555 -0
- package/dist/github/docs/octokit/DOC.md +1560 -0
- package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
- package/dist/google/docs/bigquery/python/DOC.md +1503 -0
- package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
- package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
- package/dist/huggingface/docs/transformers/DOC.md +948 -0
- package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
- package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
- package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
- package/dist/jira/docs/issues/python/DOC.md +1492 -0
- package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
- package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
- package/dist/landingai-ade/docs/api/DOC.md +620 -0
- package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
- package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
- package/dist/landingai-ade/skills/SKILL.md +489 -0
- package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
- package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
- package/dist/linear/docs/tracker/DOC.md +1554 -0
- package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
- package/dist/livekit/docs/realtime/python/DOC.md +163 -0
- package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
- package/dist/meilisearch/docs/search/DOC.md +1241 -0
- package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
- package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
- package/dist/mongodb/docs/atlas/DOC.md +2041 -0
- package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
- package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
- package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
- package/dist/okta/docs/identity/python/DOC.md +1401 -0
- package/dist/openai/docs/chat/javascript/DOC.md +407 -0
- package/dist/openai/docs/chat/python/DOC.md +568 -0
- package/dist/paypal/docs/checkout/DOC.md +278 -0
- package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
- package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
- package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
- package/dist/plaid/docs/banking/python/DOC.md +1203 -0
- package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
- package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
- package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
- package/dist/prisma/docs/orm/python/DOC.md +1317 -0
- package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
- package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
- package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
- package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
- package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
- package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
- package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
- package/dist/redis/docs/key-value/python/DOC.md +2054 -0
- package/dist/registry.json +2817 -0
- package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
- package/dist/resend/docs/email/DOC.md +1271 -0
- package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
- package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
- package/dist/search-index.json +1 -0
- package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
- package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
- package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
- package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
- package/dist/shopify/docs/storefront/DOC.md +457 -0
- package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
- package/dist/slack/docs/workspace/python/DOC.md +271 -0
- package/dist/square/docs/payments/javascript/DOC.md +1855 -0
- package/dist/square/docs/payments/python/DOC.md +1728 -0
- package/dist/stripe/docs/api/DOC.md +1727 -0
- package/dist/stripe/docs/payments/DOC.md +1726 -0
- package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
- package/dist/stytch/docs/auth/python/DOC.md +1962 -0
- package/dist/supabase/docs/client/DOC.md +1606 -0
- package/dist/twilio/docs/messaging/python/DOC.md +469 -0
- package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
- package/dist/vercel/docs/platform/DOC.md +1940 -0
- package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
- package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
- package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
- package/dist/zendesk/docs/support/python/DOC.md +2297 -0
- package/package.json +22 -6
- package/skills/get-api-docs/SKILL.md +84 -0
- package/src/commands/annotate.js +83 -0
- package/src/commands/build.js +12 -1
- package/src/commands/feedback.js +150 -0
- package/src/commands/get.js +83 -42
- package/src/commands/search.js +7 -0
- package/src/index.js +43 -17
- package/src/lib/analytics.js +90 -0
- package/src/lib/annotations.js +57 -0
- package/src/lib/bm25.js +170 -0
- package/src/lib/cache.js +69 -6
- package/src/lib/config.js +8 -3
- package/src/lib/identity.js +99 -0
- package/src/lib/registry.js +103 -20
- package/src/lib/telemetry.js +86 -0
- package/src/mcp/server.js +177 -0
- package/src/mcp/tools.js +251 -0
|
@@ -0,0 +1,933 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: workspace
|
|
3
|
+
description: "Slack Node SDK for building bots, handling workspace events, and messaging integrations"
|
|
4
|
+
metadata:
|
|
5
|
+
languages: "javascript"
|
|
6
|
+
versions: "7.12.0"
|
|
7
|
+
updated-on: "2026-03-01"
|
|
8
|
+
source: maintainer
|
|
9
|
+
tags: "slack,bot,workspace,messaging,events-api"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Slack Node SDK Examples
|
|
13
|
+
|
|
14
|
+
Examples demonstrating the official Slack Node SDK packages for JavaScript/TypeScript applications.
|
|
15
|
+
|
|
16
|
+
## Examples
|
|
17
|
+
|
|
18
|
+
- **[example/](./example/)** - Production-Ready Slack Bot
|
|
19
|
+
- Socket Mode real-time events with WebSocket connection
|
|
20
|
+
- Message echo and pattern matching
|
|
21
|
+
- User info retrieval (`users.info`)
|
|
22
|
+
- Channel listing (`conversations.list`)
|
|
23
|
+
- Automatic token refresh with OAuth v2
|
|
24
|
+
- Auto-join channels on `not_in_channel` error
|
|
25
|
+
- Comprehensive error handling with retry logic
|
|
26
|
+
- Bot message filtering to prevent infinite loops
|
|
27
|
+
- App mention handling
|
|
28
|
+
- Environment variable validation
|
|
29
|
+
- Graceful shutdown handlers
|
|
30
|
+
|
|
31
|
+
## Slack Node SDK Guidelines
|
|
32
|
+
|
|
33
|
+
<cite>
|
|
34
|
+
|
|
35
|
+
## Golden Rule: Use the Correct and Current SDK
|
|
36
|
+
|
|
37
|
+
Always use the official Slack Node SDK packages, which are the standard libraries for all Slack API interactions. Do not use legacy or deprecated packages.
|
|
38
|
+
|
|
39
|
+
- **Primary Package:** `@slack/web-api` - Official library for using the Slack Platform's Web API
|
|
40
|
+
- **OAuth Package:** `@slack/oauth` - For app installation and authentication flows
|
|
41
|
+
- **Socket Mode Package:** `@slack/socket-mode` - For real-time WebSocket connections
|
|
42
|
+
- **Legacy/Deprecated Packages:** `@slack/events-api`, `@slack/interactive-messages`, `@slack/client` should not be used
|
|
43
|
+
|
|
44
|
+
**Installation:**
|
|
45
|
+
- **Correct:** `npm install @slack/web-api`
|
|
46
|
+
- **Correct:** `npm install @slack/socket-mode @slack/oauth`
|
|
47
|
+
- **Deprecated:** `npm install @slack/events-api` (End-of-Life as of May 31st, 2021)
|
|
48
|
+
- **Deprecated:** `npm install @slack/interactive-messages` (End-of-Life as of May 31st, 2021)
|
|
49
|
+
|
|
50
|
+
**APIs and Usage:**
|
|
51
|
+
- **Correct:** `import { WebClient } from '@slack/web-api'`
|
|
52
|
+
- **Correct:** `const web = new WebClient(token)`
|
|
53
|
+
- **Correct:** `await web.chat.postMessage({ ... })`
|
|
54
|
+
- **Correct:** `import { SocketModeClient } from '@slack/socket-mode'`
|
|
55
|
+
- **Correct:** `import { InstallProvider } from '@slack/oauth'`
|
|
56
|
+
|
|
57
|
+
## System Requirements
|
|
58
|
+
|
|
59
|
+
The Slack Node SDK requires:
|
|
60
|
+
- Node.js v18 or higher
|
|
61
|
+
- npm v8.6.0 or higher
|
|
62
|
+
|
|
63
|
+
## Initialization and Authentication
|
|
64
|
+
|
|
65
|
+
### Web API Client
|
|
66
|
+
|
|
67
|
+
The `@slack/web-api` package provides the `WebClient` class for all Web API interactions.
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
import { WebClient } from '@slack/web-api';
|
|
71
|
+
|
|
72
|
+
// Initialize with bot token
|
|
73
|
+
const web = new WebClient(process.env.SLACK_BOT_TOKEN);
|
|
74
|
+
|
|
75
|
+
// Or with user token
|
|
76
|
+
const web = new WebClient(process.env.SLACK_USER_TOKEN);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Socket Mode Client
|
|
80
|
+
|
|
81
|
+
For real-time events without exposing public endpoints:
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
import { SocketModeClient } from '@slack/socket-mode';
|
|
85
|
+
|
|
86
|
+
const socketMode = new SocketModeClient({
|
|
87
|
+
appToken: process.env.SLACK_APP_TOKEN,
|
|
88
|
+
socketMode: true
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Core API Methods
|
|
93
|
+
|
|
94
|
+
### Sending Messages
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
import { WebClient } from '@slack/web-api';
|
|
98
|
+
|
|
99
|
+
const web = new WebClient(process.env.SLACK_BOT_TOKEN);
|
|
100
|
+
|
|
101
|
+
async function sendMessage() {
|
|
102
|
+
try {
|
|
103
|
+
const result = await web.chat.postMessage({
|
|
104
|
+
channel: 'C1234567890',
|
|
105
|
+
text: 'Hello world!',
|
|
106
|
+
blocks: [
|
|
107
|
+
{
|
|
108
|
+
type: 'section',
|
|
109
|
+
text: {
|
|
110
|
+
type: 'mrkdwn',
|
|
111
|
+
text: 'Hello *world*!'
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
});
|
|
116
|
+
console.log(`Message sent: ${result.ts}`);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.error('Error sending message:', error);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### File Uploads
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
import { WebClient } from '@slack/web-api';
|
|
127
|
+
import fs from 'fs';
|
|
128
|
+
|
|
129
|
+
const web = new WebClient(process.env.SLACK_BOT_TOKEN);
|
|
130
|
+
|
|
131
|
+
async function uploadFile() {
|
|
132
|
+
try {
|
|
133
|
+
const result = await web.files.uploadV2({
|
|
134
|
+
channel_id: 'C1234567890',
|
|
135
|
+
file: fs.createReadStream('./document.pdf'),
|
|
136
|
+
filename: 'document.pdf',
|
|
137
|
+
initial_comment: 'Here is the document you requested.'
|
|
138
|
+
});
|
|
139
|
+
console.log('File uploaded:', result.file.id);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error('Error uploading file:', error);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Getting User Information
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
async function getUserInfo(userId) {
|
|
150
|
+
try {
|
|
151
|
+
const result = await web.users.info({
|
|
152
|
+
user: userId
|
|
153
|
+
});
|
|
154
|
+
return result.user;
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error('Error getting user info:', error);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Listing Conversations
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
async function listChannels() {
|
|
165
|
+
try {
|
|
166
|
+
const result = await web.conversations.list({
|
|
167
|
+
types: 'public_channel,private_channel',
|
|
168
|
+
limit: 100
|
|
169
|
+
});
|
|
170
|
+
return result.channels;
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error('Error listing channels:', error);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Socket Mode for Real-time Events
|
|
178
|
+
|
|
179
|
+
Socket Mode enables real-time event handling without public endpoints. Always acknowledge events immediately.
|
|
180
|
+
|
|
181
|
+
### Basic Setup
|
|
182
|
+
|
|
183
|
+
```javascript
|
|
184
|
+
import { SocketModeClient } from '@slack/socket-mode';
|
|
185
|
+
import { WebClient } from '@slack/web-api';
|
|
186
|
+
import dotenv from 'dotenv';
|
|
187
|
+
|
|
188
|
+
dotenv.config();
|
|
189
|
+
|
|
190
|
+
// Validate required environment variables
|
|
191
|
+
if (!process.env.SLACK_BOT_TOKEN || !process.env.SLACK_APP_TOKEN) {
|
|
192
|
+
console.error('Missing required tokens');
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
let currentBotToken = process.env.SLACK_BOT_TOKEN;
|
|
197
|
+
let web = new WebClient(currentBotToken);
|
|
198
|
+
|
|
199
|
+
const socketMode = new SocketModeClient({
|
|
200
|
+
appToken: process.env.SLACK_APP_TOKEN
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Message Event Handling
|
|
205
|
+
|
|
206
|
+
Filter bot messages and handle app mentions properly:
|
|
207
|
+
|
|
208
|
+
```javascript
|
|
209
|
+
socketMode.on('message', async ({ event, ack }) => {
|
|
210
|
+
try {
|
|
211
|
+
await ack(); // Always acknowledge first
|
|
212
|
+
|
|
213
|
+
// Skip bot messages and subtypes
|
|
214
|
+
if (event.type === 'message' && !event.bot_id && event.text && event.subtype !== 'bot_message') {
|
|
215
|
+
// Skip app mentions (handled by app_mention handler)
|
|
216
|
+
if (event.subtype === 'app_mention' || (event.text && event.text.match(/<@[A-Z0-9]+>/))) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
console.log(`Message received: "${event.text}" from user: ${event.user}`);
|
|
221
|
+
|
|
222
|
+
// Process message
|
|
223
|
+
await web.chat.postMessage({
|
|
224
|
+
channel: event.channel,
|
|
225
|
+
text: `You said: "${event.text}"`
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
} catch (error) {
|
|
229
|
+
console.error('Error handling message event:', error);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### App Mention Handling
|
|
235
|
+
|
|
236
|
+
```javascript
|
|
237
|
+
socketMode.on('app_mention', async ({ event, ack }) => {
|
|
238
|
+
try {
|
|
239
|
+
await ack();
|
|
240
|
+
|
|
241
|
+
console.log(`App mention: "${event.text}" from user: ${event.user}`);
|
|
242
|
+
|
|
243
|
+
await web.chat.postMessage({
|
|
244
|
+
channel: event.channel,
|
|
245
|
+
text: `<@${event.user}> Hello!`
|
|
246
|
+
});
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error('Error handling app mention:', error);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Error Handling and Startup
|
|
254
|
+
|
|
255
|
+
```javascript
|
|
256
|
+
socketMode.on('error', (error) => {
|
|
257
|
+
console.error('Socket Mode error:', error);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
async function startBot() {
|
|
261
|
+
try {
|
|
262
|
+
console.log('Starting Slack bot...');
|
|
263
|
+
|
|
264
|
+
// Test authentication first
|
|
265
|
+
const auth = await web.auth.test();
|
|
266
|
+
console.log(`Connected as: ${auth.user} (${auth.team})`);
|
|
267
|
+
|
|
268
|
+
// Start socket mode
|
|
269
|
+
await socketMode.start();
|
|
270
|
+
console.log('Bot is running successfully!');
|
|
271
|
+
} catch (error) {
|
|
272
|
+
console.error('Error starting bot:', error);
|
|
273
|
+
|
|
274
|
+
if (error.data?.error === 'invalid_auth') {
|
|
275
|
+
console.error('Authentication failed. Check your SLACK_BOT_TOKEN.');
|
|
276
|
+
} else if (error.data?.error === 'token_expired') {
|
|
277
|
+
console.error('Token expired. Attempting refresh...');
|
|
278
|
+
// Implement token refresh logic here
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Graceful shutdown
|
|
286
|
+
process.on('SIGINT', async () => {
|
|
287
|
+
console.log('Shutting down bot...');
|
|
288
|
+
await socketMode.disconnect();
|
|
289
|
+
process.exit(0);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
process.on('SIGTERM', async () => {
|
|
293
|
+
console.log('Shutting down bot...');
|
|
294
|
+
await socketMode.disconnect();
|
|
295
|
+
process.exit(0);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
startBot();
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## OAuth Installation Flow
|
|
302
|
+
|
|
303
|
+
```javascript
|
|
304
|
+
import { InstallProvider } from '@slack/oauth';
|
|
305
|
+
import { WebClient } from '@slack/web-api';
|
|
306
|
+
|
|
307
|
+
const installer = new InstallProvider({
|
|
308
|
+
clientId: process.env.SLACK_CLIENT_ID,
|
|
309
|
+
clientSecret: process.env.SLACK_CLIENT_SECRET,
|
|
310
|
+
stateSecret: 'my-state-secret',
|
|
311
|
+
installationStore: {
|
|
312
|
+
storeInstallation: async (installation) => {
|
|
313
|
+
// Store installation data in your database
|
|
314
|
+
console.log('Installation:', installation);
|
|
315
|
+
},
|
|
316
|
+
fetchInstallation: async (installQuery) => {
|
|
317
|
+
// Fetch installation data from your database
|
|
318
|
+
return installation;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// Generate install URL
|
|
324
|
+
const installUrl = await installer.generateInstallUrl({
|
|
325
|
+
scopes: ['chat:write', 'channels:read'],
|
|
326
|
+
userScopes: ['chat:write']
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Handle OAuth callback
|
|
330
|
+
app.get('/slack/oauth_redirect', async (req, res) => {
|
|
331
|
+
try {
|
|
332
|
+
const installation = await installer.handleCallback(req, res);
|
|
333
|
+
console.log('Installation successful:', installation);
|
|
334
|
+
} catch (error) {
|
|
335
|
+
console.error('OAuth error:', error);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Error Handling and Resilience
|
|
341
|
+
|
|
342
|
+
### Basic Error Handling
|
|
343
|
+
|
|
344
|
+
All API responses follow a consistent structure:
|
|
345
|
+
|
|
346
|
+
```javascript
|
|
347
|
+
import { WebClient } from '@slack/web-api';
|
|
348
|
+
|
|
349
|
+
const web = new WebClient(process.env.SLACK_BOT_TOKEN);
|
|
350
|
+
|
|
351
|
+
async function handleApiCall() {
|
|
352
|
+
try {
|
|
353
|
+
const result = await web.chat.postMessage({
|
|
354
|
+
channel: 'C1234567890',
|
|
355
|
+
text: 'Hello!'
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
if (result.ok) {
|
|
359
|
+
console.log('Success:', result.ts);
|
|
360
|
+
} else {
|
|
361
|
+
console.error('API Error:', result.error);
|
|
362
|
+
}
|
|
363
|
+
} catch (error) {
|
|
364
|
+
if (error.code === 'slack_webapi_platform_error') {
|
|
365
|
+
console.error('Slack API Error:', error.data);
|
|
366
|
+
} else {
|
|
367
|
+
console.error('Network/Other Error:', error);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Token Refresh Pattern
|
|
374
|
+
|
|
375
|
+
Implement automatic token refresh for OAuth apps:
|
|
376
|
+
|
|
377
|
+
```javascript
|
|
378
|
+
import fs from 'fs';
|
|
379
|
+
|
|
380
|
+
async function refreshBotToken() {
|
|
381
|
+
try {
|
|
382
|
+
console.log('Refreshing bot token...');
|
|
383
|
+
const response = await fetch('https://slack.com/api/oauth.v2.access', {
|
|
384
|
+
method: 'POST',
|
|
385
|
+
headers: {
|
|
386
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
387
|
+
},
|
|
388
|
+
body: new URLSearchParams({
|
|
389
|
+
client_id: process.env.SLACK_CLIENT_ID,
|
|
390
|
+
client_secret: process.env.SLACK_CLIENT_SECRET,
|
|
391
|
+
grant_type: 'refresh_token',
|
|
392
|
+
refresh_token: process.env.SLACK_BOT_REFRESH_TOKEN,
|
|
393
|
+
}),
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
const data = await response.json();
|
|
397
|
+
|
|
398
|
+
if (data.ok) {
|
|
399
|
+
currentBotToken = data.access_token;
|
|
400
|
+
web = new WebClient(currentBotToken);
|
|
401
|
+
|
|
402
|
+
// Update .env file with new token
|
|
403
|
+
const envContent = fs.readFileSync('.env', 'utf8');
|
|
404
|
+
const updatedContent = envContent.replace(
|
|
405
|
+
/SLACK_BOT_TOKEN=.*/,
|
|
406
|
+
`SLACK_BOT_TOKEN=${currentBotToken}`
|
|
407
|
+
);
|
|
408
|
+
fs.writeFileSync('.env', updatedContent);
|
|
409
|
+
|
|
410
|
+
console.log('Bot token refreshed successfully');
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
return false;
|
|
414
|
+
} catch (error) {
|
|
415
|
+
console.error('Error refreshing token:', error);
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Retry Pattern for Token Errors
|
|
422
|
+
|
|
423
|
+
Automatically retry API calls after token refresh:
|
|
424
|
+
|
|
425
|
+
```javascript
|
|
426
|
+
async function sendMessage(channel, text) {
|
|
427
|
+
try {
|
|
428
|
+
const result = await web.chat.postMessage({
|
|
429
|
+
channel: channel,
|
|
430
|
+
text: text
|
|
431
|
+
});
|
|
432
|
+
console.log(`Message sent: ${text}`);
|
|
433
|
+
return result;
|
|
434
|
+
} catch (error) {
|
|
435
|
+
console.error('Error sending message:', error);
|
|
436
|
+
|
|
437
|
+
// Handle token-related errors with refresh and retry
|
|
438
|
+
if (error.data?.error === 'invalid_auth' ||
|
|
439
|
+
error.data?.error === 'token_revoked' ||
|
|
440
|
+
error.data?.error === 'token_expired') {
|
|
441
|
+
console.log('Token appears invalid, attempting refresh...');
|
|
442
|
+
const refreshed = await refreshBotToken();
|
|
443
|
+
if (refreshed) {
|
|
444
|
+
// Retry the message after token refresh
|
|
445
|
+
try {
|
|
446
|
+
const result = await web.chat.postMessage({
|
|
447
|
+
channel: channel,
|
|
448
|
+
text: text
|
|
449
|
+
});
|
|
450
|
+
console.log(`Message sent after token refresh: ${result.ts}`);
|
|
451
|
+
return result;
|
|
452
|
+
} catch (retryError) {
|
|
453
|
+
console.error('Failed to send message even after token refresh:', retryError);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
throw error;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Auto-Join Channel Pattern
|
|
464
|
+
|
|
465
|
+
Handle `not_in_channel` errors by joining automatically:
|
|
466
|
+
|
|
467
|
+
```javascript
|
|
468
|
+
async function ensureBotInChannel(channel) {
|
|
469
|
+
try {
|
|
470
|
+
const result = await web.conversations.join({ channel });
|
|
471
|
+
if (result.ok) {
|
|
472
|
+
console.log(`Joined channel ${channel} before sending message`);
|
|
473
|
+
return true;
|
|
474
|
+
}
|
|
475
|
+
} catch (error) {
|
|
476
|
+
if (error.data?.error === 'already_in_channel') {
|
|
477
|
+
return true;
|
|
478
|
+
}
|
|
479
|
+
if (error.data?.error === 'method_not_supported_for_channel_type') {
|
|
480
|
+
console.warn(`Cannot join channel type for ${channel}; proceeding without join.`);
|
|
481
|
+
} else {
|
|
482
|
+
console.error('Error joining channel:', error);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
async function sendMessage(channel, text) {
|
|
489
|
+
try {
|
|
490
|
+
return await web.chat.postMessage({ channel, text });
|
|
491
|
+
} catch (error) {
|
|
492
|
+
// Handle not_in_channel error
|
|
493
|
+
if (error.data?.error === 'not_in_channel') {
|
|
494
|
+
console.log(`Bot is not in channel ${channel}, attempting to join...`);
|
|
495
|
+
const joined = await ensureBotInChannel(channel);
|
|
496
|
+
if (joined) {
|
|
497
|
+
try {
|
|
498
|
+
const result = await web.chat.postMessage({ channel, text });
|
|
499
|
+
console.log(`Message sent after joining channel: ${result.ts}`);
|
|
500
|
+
return result;
|
|
501
|
+
} catch (retryError) {
|
|
502
|
+
console.error('Failed to send message even after joining channel:', retryError);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
throw error;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### Combined Error Handling Pattern
|
|
512
|
+
|
|
513
|
+
Apply retry logic to all API methods:
|
|
514
|
+
|
|
515
|
+
```javascript
|
|
516
|
+
async function getUserInfo(userId) {
|
|
517
|
+
try {
|
|
518
|
+
const result = await web.users.info({ user: userId });
|
|
519
|
+
return result.user;
|
|
520
|
+
} catch (error) {
|
|
521
|
+
console.error('Error getting user info:', error);
|
|
522
|
+
|
|
523
|
+
// Retry with token refresh if needed
|
|
524
|
+
if (error.data?.error === 'invalid_auth' ||
|
|
525
|
+
error.data?.error === 'token_revoked' ||
|
|
526
|
+
error.data?.error === 'token_expired') {
|
|
527
|
+
console.log('Token appears invalid, attempting refresh...');
|
|
528
|
+
const refreshed = await refreshBotToken();
|
|
529
|
+
if (refreshed) {
|
|
530
|
+
try {
|
|
531
|
+
const result = await web.users.info({ user: userId });
|
|
532
|
+
return result.user;
|
|
533
|
+
} catch (retryError) {
|
|
534
|
+
console.error('Failed to get user info even after token refresh:', retryError);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
async function listChannels() {
|
|
542
|
+
try {
|
|
543
|
+
const result = await web.conversations.list({
|
|
544
|
+
types: 'public_channel,private_channel',
|
|
545
|
+
limit: 100
|
|
546
|
+
});
|
|
547
|
+
return result.channels;
|
|
548
|
+
} catch (error) {
|
|
549
|
+
console.error('Error listing channels:', error);
|
|
550
|
+
|
|
551
|
+
// Retry with token refresh if needed
|
|
552
|
+
if (error.data?.error === 'invalid_auth' ||
|
|
553
|
+
error.data?.error === 'token_revoked' ||
|
|
554
|
+
error.data?.error === 'token_expired') {
|
|
555
|
+
console.log('Token appears invalid, attempting refresh...');
|
|
556
|
+
const refreshed = await refreshBotToken();
|
|
557
|
+
if (refreshed) {
|
|
558
|
+
try {
|
|
559
|
+
const result = await web.conversations.list({
|
|
560
|
+
types: 'public_channel,private_channel',
|
|
561
|
+
limit: 100
|
|
562
|
+
});
|
|
563
|
+
return result.channels;
|
|
564
|
+
} catch (retryError) {
|
|
565
|
+
console.error('Failed to list channels even after token refresh:', retryError);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
## Pagination
|
|
574
|
+
|
|
575
|
+
```javascript
|
|
576
|
+
async function getAllChannels() {
|
|
577
|
+
const channels = [];
|
|
578
|
+
let cursor;
|
|
579
|
+
|
|
580
|
+
do {
|
|
581
|
+
const result = await web.conversations.list({
|
|
582
|
+
types: 'public_channel,private_channel',
|
|
583
|
+
limit: 200,
|
|
584
|
+
cursor: cursor
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
channels.push(...result.channels);
|
|
588
|
+
cursor = result.response_metadata?.next_cursor;
|
|
589
|
+
} while (cursor);
|
|
590
|
+
|
|
591
|
+
return channels;
|
|
592
|
+
}
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
## Rate Limiting
|
|
596
|
+
|
|
597
|
+
The SDK automatically handles rate limiting with built-in retry logic:
|
|
598
|
+
|
|
599
|
+
```javascript
|
|
600
|
+
import { WebClient } from '@slack/web-api';
|
|
601
|
+
|
|
602
|
+
const web = new WebClient(process.env.SLACK_BOT_TOKEN, {
|
|
603
|
+
retryConfig: {
|
|
604
|
+
retries: 3,
|
|
605
|
+
factor: 2
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
## TypeScript Support
|
|
611
|
+
|
|
612
|
+
The SDK includes full TypeScript definitions:
|
|
613
|
+
|
|
614
|
+
```typescript
|
|
615
|
+
import { WebClient, ChatPostMessageResponse } from '@slack/web-api';
|
|
616
|
+
|
|
617
|
+
const web = new WebClient(process.env.SLACK_BOT_TOKEN);
|
|
618
|
+
|
|
619
|
+
async function sendTypedMessage(): Promise<ChatPostMessageResponse> {
|
|
620
|
+
return await web.chat.postMessage({
|
|
621
|
+
channel: 'C1234567890',
|
|
622
|
+
text: 'Hello TypeScript!'
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
## Webhook Usage
|
|
628
|
+
|
|
629
|
+
```javascript
|
|
630
|
+
import { IncomingWebhook } from '@slack/webhook';
|
|
631
|
+
|
|
632
|
+
const webhook = new IncomingWebhook(process.env.SLACK_WEBHOOK_URL);
|
|
633
|
+
|
|
634
|
+
async function sendWebhookMessage() {
|
|
635
|
+
await webhook.send({
|
|
636
|
+
text: 'Hello from webhook!',
|
|
637
|
+
attachments: [
|
|
638
|
+
{
|
|
639
|
+
color: 'good',
|
|
640
|
+
text: 'This is an attachment'
|
|
641
|
+
}
|
|
642
|
+
]
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
## Block Kit Messages
|
|
648
|
+
|
|
649
|
+
```javascript
|
|
650
|
+
async function sendRichMessage() {
|
|
651
|
+
await web.chat.postMessage({
|
|
652
|
+
channel: 'C1234567890',
|
|
653
|
+
blocks: [
|
|
654
|
+
{
|
|
655
|
+
type: 'header',
|
|
656
|
+
text: {
|
|
657
|
+
type: 'plain_text',
|
|
658
|
+
text: 'Welcome!'
|
|
659
|
+
}
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
type: 'section',
|
|
663
|
+
text: {
|
|
664
|
+
type: 'mrkdwn',
|
|
665
|
+
text: 'This is a *rich* message with buttons.'
|
|
666
|
+
},
|
|
667
|
+
accessory: {
|
|
668
|
+
type: 'button',
|
|
669
|
+
text: {
|
|
670
|
+
type: 'plain_text',
|
|
671
|
+
text: 'Click Me'
|
|
672
|
+
},
|
|
673
|
+
action_id: 'button_click'
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
]
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
## Production Best Practices
|
|
682
|
+
|
|
683
|
+
### Environment Variable Validation
|
|
684
|
+
|
|
685
|
+
Always validate required environment variables at startup:
|
|
686
|
+
|
|
687
|
+
```javascript
|
|
688
|
+
import dotenv from 'dotenv';
|
|
689
|
+
|
|
690
|
+
dotenv.config();
|
|
691
|
+
|
|
692
|
+
// Validate required environment variables
|
|
693
|
+
if (!process.env.SLACK_BOT_TOKEN) {
|
|
694
|
+
console.error('❌ SLACK_BOT_TOKEN is required. Please check your .env file.');
|
|
695
|
+
process.exit(1);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
if (!process.env.SLACK_APP_TOKEN) {
|
|
699
|
+
console.error('❌ SLACK_APP_TOKEN is required. Please check your .env file.');
|
|
700
|
+
process.exit(1);
|
|
701
|
+
}
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
### Token Management
|
|
705
|
+
|
|
706
|
+
Use mutable references for tokens that may need refreshing:
|
|
707
|
+
|
|
708
|
+
```javascript
|
|
709
|
+
let currentBotToken = process.env.SLACK_BOT_TOKEN;
|
|
710
|
+
let web = new WebClient(currentBotToken);
|
|
711
|
+
|
|
712
|
+
// After refresh, reassign both:
|
|
713
|
+
currentBotToken = data.access_token;
|
|
714
|
+
web = new WebClient(currentBotToken);
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
### Comprehensive Startup Flow
|
|
718
|
+
|
|
719
|
+
Test authentication before starting Socket Mode:
|
|
720
|
+
|
|
721
|
+
```javascript
|
|
722
|
+
async function startBot() {
|
|
723
|
+
try {
|
|
724
|
+
console.log('🤖 Starting Slack bot...');
|
|
725
|
+
|
|
726
|
+
// Test authentication first
|
|
727
|
+
console.log('🔐 Testing authentication...');
|
|
728
|
+
const auth = await web.auth.test();
|
|
729
|
+
console.log(`✅ Connected as: ${auth.user} (${auth.team})`);
|
|
730
|
+
|
|
731
|
+
// Start socket mode connection
|
|
732
|
+
console.log('🔌 Starting Socket Mode connection...');
|
|
733
|
+
await socketMode.start();
|
|
734
|
+
|
|
735
|
+
console.log('🚀 Bot is running successfully!');
|
|
736
|
+
console.log('📝 Available commands:');
|
|
737
|
+
console.log(' - Send messages containing: "hello", "info", "channels"');
|
|
738
|
+
console.log(' - Mention the bot in any channel');
|
|
739
|
+
|
|
740
|
+
} catch (error) {
|
|
741
|
+
console.error('❌ Error starting bot:', error);
|
|
742
|
+
|
|
743
|
+
// Provide helpful error messages based on error type
|
|
744
|
+
if (error.data?.error === 'invalid_auth') {
|
|
745
|
+
console.error('🔑 Authentication failed. Please check your SLACK_BOT_TOKEN.');
|
|
746
|
+
} else if (error.data?.error === 'token_revoked') {
|
|
747
|
+
console.error('🔑 Token has been revoked. Please generate a new SLACK_BOT_TOKEN.');
|
|
748
|
+
} else if (error.data?.error === 'token_expired') {
|
|
749
|
+
console.error('🔑 Token has expired. Attempting to refresh...');
|
|
750
|
+
const refreshed = await refreshBotToken();
|
|
751
|
+
if (refreshed) {
|
|
752
|
+
console.log('✅ Token refreshed successfully. Restarting bot...');
|
|
753
|
+
return startBot(); // Restart with new token
|
|
754
|
+
}
|
|
755
|
+
} else if (error.message?.includes('invalid_app_token')) {
|
|
756
|
+
console.error('🔑 Invalid app token. Please check your SLACK_APP_TOKEN.');
|
|
757
|
+
} else if (error.code === 'ENOTFOUND') {
|
|
758
|
+
console.error('🌐 Network error. Please check your internet connection.');
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
console.log('\n💡 Make sure you have:');
|
|
762
|
+
console.log(' 1. Created a .env file with valid tokens');
|
|
763
|
+
console.log(' 2. Enabled Socket Mode in your Slack app');
|
|
764
|
+
console.log(' 3. Added the required OAuth scopes');
|
|
765
|
+
|
|
766
|
+
process.exit(1);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
startBot();
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
### Graceful Shutdown Handlers
|
|
774
|
+
|
|
775
|
+
Always disconnect Socket Mode cleanly:
|
|
776
|
+
|
|
777
|
+
```javascript
|
|
778
|
+
process.on('SIGINT', async () => {
|
|
779
|
+
console.log('\nShutting down bot...');
|
|
780
|
+
await socketMode.disconnect();
|
|
781
|
+
process.exit(0);
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
process.on('SIGTERM', async () => {
|
|
785
|
+
console.log('\nShutting down bot...');
|
|
786
|
+
await socketMode.disconnect();
|
|
787
|
+
process.exit(0);
|
|
788
|
+
});
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
### Message Filtering Best Practices
|
|
792
|
+
|
|
793
|
+
Prevent infinite loops and duplicate processing:
|
|
794
|
+
|
|
795
|
+
```javascript
|
|
796
|
+
socketMode.on('message', async ({ event, ack }) => {
|
|
797
|
+
try {
|
|
798
|
+
await ack(); // Always acknowledge first
|
|
799
|
+
|
|
800
|
+
// Filter out bot messages, subtypes, and app mentions
|
|
801
|
+
if (event.type === 'message' &&
|
|
802
|
+
!event.bot_id &&
|
|
803
|
+
event.text &&
|
|
804
|
+
event.subtype !== 'bot_message') {
|
|
805
|
+
|
|
806
|
+
// Skip app mentions (handled by app_mention handler)
|
|
807
|
+
if (event.subtype === 'app_mention' ||
|
|
808
|
+
(event.text && event.text.match(/<@[A-Z0-9]+>/))) {
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// Process the message
|
|
813
|
+
// ...
|
|
814
|
+
}
|
|
815
|
+
} catch (error) {
|
|
816
|
+
console.error('Error handling message event:', error);
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
### Error Recovery Strategies
|
|
822
|
+
|
|
823
|
+
Implement comprehensive error recovery:
|
|
824
|
+
|
|
825
|
+
```javascript
|
|
826
|
+
async function sendMessage(channel, text) {
|
|
827
|
+
try {
|
|
828
|
+
const result = await web.chat.postMessage({
|
|
829
|
+
channel: channel,
|
|
830
|
+
text: text
|
|
831
|
+
});
|
|
832
|
+
console.log(`Message sent: ${text}`);
|
|
833
|
+
return result;
|
|
834
|
+
} catch (error) {
|
|
835
|
+
console.error('Error sending message:', error);
|
|
836
|
+
|
|
837
|
+
// 1. Handle token errors
|
|
838
|
+
if (error.data?.error === 'invalid_auth' ||
|
|
839
|
+
error.data?.error === 'token_revoked' ||
|
|
840
|
+
error.data?.error === 'token_expired') {
|
|
841
|
+
const refreshed = await refreshBotToken();
|
|
842
|
+
if (refreshed) {
|
|
843
|
+
return await web.chat.postMessage({ channel, text });
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
// 2. Handle channel access errors
|
|
847
|
+
else if (error.data?.error === 'not_in_channel') {
|
|
848
|
+
const joined = await ensureBotInChannel(channel);
|
|
849
|
+
if (joined) {
|
|
850
|
+
return await web.chat.postMessage({ channel, text });
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// Don't try to send error messages for auth/channel errors
|
|
855
|
+
if (!['invalid_auth', 'token_revoked', 'token_expired', 'not_in_channel'].includes(error.data?.error)) {
|
|
856
|
+
try {
|
|
857
|
+
await web.chat.postMessage({
|
|
858
|
+
channel: channel,
|
|
859
|
+
text: `⚠️ Error occurred: ${error.message}`
|
|
860
|
+
});
|
|
861
|
+
} catch (secondError) {
|
|
862
|
+
console.error('Failed to send error message to channel:', secondError);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
throw error;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
## Notes
|
|
872
|
+
|
|
873
|
+
The Slack Node SDK is modular and designed for specific use cases. <cite/> The `@slack/web-api` package is the primary interface for most Slack API interactions, while `@slack/socket-mode` enables real-time communication without exposing public endpoints. <cite/> Legacy packages like `@slack/events-api` and `@slack/interactive-messages` reached End-of-Life on May 31st, 2021 and should be migrated to Socket Mode or the Bolt framework. <cite/> The SDK includes comprehensive TypeScript support and handles rate limiting, retries, and pagination automatically. <cite/>
|
|
874
|
+
|
|
875
|
+
Wiki pages you might want to explore:
|
|
876
|
+
- [Overview (slackapi/node-slack-sdk)](/wiki/slackapi/node-slack-sdk#1)
|
|
877
|
+
|
|
878
|
+
### Citations
|
|
879
|
+
|
|
880
|
+
```json
|
|
881
|
+
"name": "@slack/web-api",
|
|
882
|
+
"version": "7.9.2",
|
|
883
|
+
"description": "Official library for using the Slack Platform's Web API",
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
```json
|
|
887
|
+
"types": "./dist/index.d.ts",
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
```json
|
|
891
|
+
"engines": {
|
|
892
|
+
"node": ">= 18",
|
|
893
|
+
"npm": ">= 8.6.0"
|
|
894
|
+
},
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
```json
|
|
898
|
+
"version": "2.0.2",
|
|
899
|
+
"description": "Official library for using the Slack Platform's Interactive Buttons, Menus, Dialogs, Actions, and Block Actions",
|
|
900
|
+
"author": "Slack Technologies, LLC",
|
|
901
|
+
"license": "MIT",
|
|
902
|
+
"keywords": [
|
|
903
|
+
"slack",
|
|
904
|
+
"interactive",
|
|
905
|
+
"interactive-messages",
|
|
906
|
+
"interactive-components",
|
|
907
|
+
"dialog",
|
|
908
|
+
"button",
|
|
909
|
+
"menu",
|
|
910
|
+
"action",
|
|
911
|
+
"block-kit",
|
|
912
|
+
"block-actions",
|
|
913
|
+
"bot",
|
|
914
|
+
"server",
|
|
915
|
+
"http",
|
|
916
|
+
"api",
|
|
917
|
+
"verify",
|
|
918
|
+
"signature",
|
|
919
|
+
"request-signing"
|
|
920
|
+
],
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
```typescript
|
|
924
|
+
import type { WebAPICallResult } from '../../WebClient';
|
|
925
|
+
export type AuthTeamsListResponse = WebAPICallResult & {
|
|
926
|
+
error?: string;
|
|
927
|
+
needed?: string;
|
|
928
|
+
ok?: boolean;
|
|
929
|
+
provided?: string;
|
|
930
|
+
response_metadata?: ResponseMetadata;
|
|
931
|
+
teams?: Team[];
|
|
932
|
+
};
|
|
933
|
+
```
|