buster/packages/slack
Nate Kelley 9b8f039d37
Update dockerfile
2025-06-26 12:32:47 -06:00
..
scripts Update dockerfile 2025-06-26 12:32:47 -06:00
src Add integration tests for Slack handler and refactor Slack OAuth service 2025-06-26 10:15:22 -06:00
.env.example all test passing and scopes set for slack 2025-06-25 15:22:24 -06:00
.gitignore Enhance Slack package documentation and structure; add OAuth and messaging features. Update package dependencies and improve code formatting in related files. 2025-06-25 09:25:07 -06:00
CLAUDE.md Enhance Slack package documentation and features; add dependency injection for WebClient, improve type safety by removing `any` and `unknown` types, and update error handling patterns. Revise testing guidelines and provide example test setups. Update README to reflect new capabilities and usage patterns. 2025-06-25 10:40:44 -06:00
README.md last few tests and such. 2025-06-25 15:31:58 -06:00
biome.json new claude.md and added the slack package 2025-06-25 08:00:38 -06:00
env.d.ts new claude.md and added the slack package 2025-06-25 08:00:38 -06:00
package.json fix broken types 2025-06-25 15:51:59 -06:00
tsconfig.json new claude.md and added the slack package 2025-06-25 08:00:38 -06:00
vitest.config.ts Refactor Slack service classes to accept optional WebClient instances, improve error handling, and enhance environment validation logging. Update message formatting to use specific types for blocks and attachments. Remove outdated test files for auth, channels, and messaging services. 2025-06-25 10:19:44 -06:00

README.md

@buster/slack

Standalone Slack integration package with OAuth 2.0, messaging, and channel management capabilities.

Installation

pnpm add @buster/slack

Features

  • OAuth 2.0 Authentication - Complete OAuth flow with CSRF protection
  • Channel Management - List, validate, join, and leave channels
  • Messaging - Send messages with blocks, attachments, and threading support
  • Type Safety - Full TypeScript support with no any types
  • Zod Validation - Runtime validation for all inputs
  • Dependency Injection - Easy testing with injectable WebClient
  • Framework Agnostic - Works with any Node.js framework
  • Storage Interfaces - Bring your own storage implementation
  • Retry Logic - Automatic retry with exponential backoff
  • Error Handling - Typed errors with user-friendly messages

Usage

OAuth Authentication

import { SlackAuthService, ISlackTokenStorage, ISlackOAuthStateStorage } from '@buster/slack';

// Implement storage interfaces
const tokenStorage: ISlackTokenStorage = { /* your implementation */ };
const stateStorage: ISlackOAuthStateStorage = { /* your implementation */ };

const authService = new SlackAuthService(
  {
    clientId: 'your-client-id',
    clientSecret: 'your-client-secret',
    redirectUri: 'https://your-app.com/slack/callback',
    scopes: ['channels:read', 'chat:write'],
  },
  tokenStorage,
  stateStorage
);

// Generate OAuth URL
const { authUrl, state } = await authService.generateAuthUrl({ userId: 'user-123' });

// Handle OAuth callback
const result = await authService.handleCallback(code, state, 'user-123');

Channel Management

import { SlackChannelService } from '@buster/slack';

const channelService = new SlackChannelService();

// Get available channels
const channels = await channelService.getAvailableChannels(accessToken);

// Validate channel access
const channel = await channelService.validateChannelAccess(accessToken, channelId);

// Join a channel
const { success } = await channelService.joinChannel(accessToken, channelId);

// Leave a channel
const leaveResult = await channelService.leaveChannel(accessToken, channelId);

Sending Messages

import { SlackMessagingService } from '@buster/slack';

const messagingService = new SlackMessagingService();

// Send a simple message
const result = await messagingService.sendMessage(
  accessToken,
  channelId,
  { text: 'Hello, Slack!' }
);

// Send a message with blocks
const richMessage = await messagingService.sendMessage(
  accessToken,
  channelId,
  {
    blocks: [
      {
        type: 'section',
        text: {
          type: 'mrkdwn',
          text: '*Important Update*\nThis is a formatted message.',
        },
      },
    ],
  }
);

// Send with automatic retry
const retryResult = await messagingService.sendMessageWithRetry(
  accessToken,
  channelId,
  { text: 'Important notification' },
  3 // max retries
);

Threading and Replies

// Send initial message
const { messageTs } = await messagingService.sendMessage(
  accessToken,
  channelId,
  { text: 'Deployment started...' }
);

// Reply to the message
await messagingService.replyToMessage(
  accessToken,
  channelId,
  messageTs,
  { text: 'Deployment completed successfully!' }
);

Message Tracking

Implement the ISlackMessageTracking interface to enable message tracking for threading:

import { ISlackMessageTracking, MessageTrackingData } from '@buster/slack';

class MyMessageTracking implements ISlackMessageTracking {
  async storeMessageTracking(trackingData: MessageTrackingData): Promise<void> {
    // Store message mapping in your database
  }
  
  async getMessageTracking(internalMessageId: string): Promise<MessageTrackingData | null> {
    // Retrieve message mapping
  }
  
  // ... other methods
}

Message Formatting Utilities

import { 
  formatSimpleMessage, 
  formatBlockMessage,
  createSectionBlock,
  createActionsBlock,
  createContextBlock,
  createDividerBlock,
  MessageTemplates 
} from '@buster/slack';

// Simple message
const message = formatSimpleMessage('Hello, Slack!');

// Message with blocks
const blockMessage = formatBlockMessage([
  createSectionBlock('*Important Announcement*', { type: 'mrkdwn' }),
  createDividerBlock(),
  createContextBlock(['Posted by bot at ' + new Date().toISOString()])
]);

// Deployment notification template
const deploymentMessage = MessageTemplates.deployment({
  project: 'my-app',
  environment: 'production',
  version: '1.2.3',
  status: 'success',
  duration: '2m 30s',
  url: 'https://example.com/deployments/123'
});

// Alert message template
const alertMessage = MessageTemplates.alert({
  title: 'High CPU Usage',
  message: 'CPU usage exceeded 90% threshold',
  severity: 'warning',
  source: 'monitoring-system',
  actions: [
    { text: 'View Dashboard', url: 'https://example.com/dashboard' }
  ]
});

// Review flagging template
const reviewMessage = MessageTemplates.reviewFlag({
  reviewerName: 'Jane Doe',
  profileUrl: 'https://example.com/profile/jane',
  issueTitle: 'Data Quality Issue',
  description: 'The query returned unexpected results that may indicate a data integrity problem.'
});

// Update and delete messages
await messagingService.updateMessage(accessToken, channelId, messageTs, {
  text: 'Updated message content'
});

await messagingService.deleteMessage(accessToken, channelId, messageTs);

Testing

The package includes comprehensive test coverage and supports dependency injection for easy testing:

import { SlackMessagingService } from '@buster/slack';
import { WebClient } from '@slack/web-api';

// Create a mock WebClient for testing
const mockClient = {
  chat: {
    postMessage: jest.fn().mockResolvedValue({ ok: true, ts: '123' })
  }
} as unknown as WebClient;

// Inject the mock client
const messagingService = new SlackMessagingService(mockClient);

// Test your integration
const result = await messagingService.sendMessage(
  'test-token',
  'C123',
  { text: 'Test message' }
);

Error Handling

All services provide typed errors for different scenarios:

import { SlackIntegrationError } from '@buster/slack';

try {
  await messagingService.sendMessage(token, channelId, message);
} catch (error) {
  if (error instanceof SlackIntegrationError) {
    switch (error.code) {
      case 'INVALID_TOKEN':
        // Handle invalid token
        break;
      case 'CHANNEL_NOT_FOUND':
        // Handle channel not found
        break;
      case 'RATE_LIMITED':
        // Handle rate limiting
        break;
    }
  }
}

Development

# Install dependencies
pnpm install

# Type check
pnpm run typecheck

# Build
pnpm run build

# Test
pnpm run test

# Lint and format
pnpm run check:fix

Integration Testing

Want to test against your real Slack workspace? It's super easy:

  1. Create a Slack app at https://api.slack.com/apps
  2. Copy .env.example to .env
  3. Add just 2 values:
    • SLACK_BOT_TOKEN - Your bot token from OAuth & Permissions page
    • SLACK_CHANNEL_ID - Any channel ID (right-click channel → View details)
  4. Run the tests:
    pnpm run test:integration
    

That's it! The integration tests will:

  • Validate your bot token
  • Check channel access
  • Send test messages
  • Test message updates
  • Test threading
  • List available channels
  • Test OAuth flows
  • Test error handling

All tests run in seconds and show clear pass/fail results. Integration tests are in .int.test.ts files alongside the service files.

Architecture

This package follows a clean architecture with:

  • Services - Core business logic (auth, channels, messaging)
  • Interfaces - Storage contracts for tokens and state
  • Types - TypeScript types and Zod schemas
  • Utils - Helper functions and formatters
  • Mocks - Testing utilities

All code is strictly typed with no any or unknown types, ensuring type safety throughout your application.