This is a TypeScript-based modular Slack bot framework that powers various bot functionalities in the TSG Slack workspace. The system uses a plugin-based architecture where each bot is implemented as a separate plugin. Please follow these guidelines when contributing:
- Plugin System: Modular bot plugins loaded dynamically from individual directories
- Slack Clients: Uses
@slack/web-api,@slack/events-api, and@slack/interactive-messages - Fastify Server: HTTP server handling Slack events and interactive messages
- State Management: Persistent storage system for plugin data using the
lib/statemodule - Logging: Structured logging with Winston logger from
lib/logger
Each plugin should follow this pattern:
import type {SlackInterface} from '../lib/slack';
// Default export for event handling
export default async ({webClient, eventClient, messageClient}: SlackInterface) => {
// Plugin initialization logic
};
// Optional server export for HTTP endpoints
import plugin from 'fastify-plugin';
import type {FastifyPluginCallback} from 'fastify';
export const server = ({webClient, eventClient, messageClient}: SlackInterface) => {
const callback: FastifyPluginCallback = async (fastify, opts, next) => {
// Register HTTP endpoints
next();
};
return plugin(callback);
};index.ts: Main entry point that loads and initializes plugins- Individual plugin directories: Each contains a self-contained bot implementation
bin/: Command-line tools and scriptslib/: Core utilities (state management, logging, etc.)functions/: Firebase functions package
- Use TypeScript for new code, with proper type definitions
- Follow existing code patterns and structure
- Use modern JavaScript/TypeScript features whenever possible
- Implement proper error handling and logging using the shared logger
- When adding or editing the dependencies, make sure not to edit
package.jsonorpackage-lock.jsondirectly. Instead, use the propernpmcommands - Use the shared
SlackInterfacetype for Slack client interactions - When adding new environment variables, make sure to document them in the
.env.examplefile - Explicitly using
anytype should be strongly avoided. Instead, use more specific types whenever possible - Type casting with
askeyword should be used judiciously and only when necessary
The code under the helloworld/ directory is not intended to be used in production, but is intended to demonstrate coding and testing best practices for developers.
The code in this repository is a mix of modern and recommended syntax and old and deprecated syntax. The helloworld/ directory is maintained to always be the latest and recommended syntax for slackbots, so if you're not sure what to do, follow the helloworld bot's syntax.
Don't add useless comments that state the obvious or repeat the code. Focus on explaining the "why" behind complex logic. For example:
// Calculate the 10th Fibonacci number
const nextFibonacci = fibonacciCalculator(10);is not necessary because the code is self-explanatory. Instead, you must just remove it, or focus on explaining the purpose of the calculation or any non-obvious logic.
- Write tests using Jest framework
- Follow existing test patterns and structure
- Use the provided
SlackMockclass for testing Slack interactions - Place tests in
*.test.tsfiles - Run tests with
npm test -- [<test-file>]
- Run development server:
npm run dev - Run specific plugin only:
npm run dev -- --only [bot-id] - Production mode:
npm start
Note: Local debugging requires
CLAUDE.local.mdto exist in the project root. If it does not exist, local debugging is not configured and these instructions cannot be followed. CopyCLAUDE.local.md.exampletoCLAUDE.local.mdand ask user to fill in the appropriate environment values before proceeding.
See CLAUDE.local.md for environment-specific values (ngrok domain, port, channel IDs, etc.).
Launch the following two commands simultaneously in the background when debugging.
ngrok http --domain=<NGROK_DOMAIN> <PORT> > .logs/ngrok.log 2>&1 & echo "Ngrok PID: $!"Required to forward Slack Events API requests to localhost.
npm run dev -- --only <bot-id> > .logs/<bot-id>.log 2>&1 & echo "App PID: $!"- The
--onlyflag is mandatory. Omitting it starts all plugins simultaneously, making the app extremely slow. <bot-id>matches the plugin's directory name (e.g.,sushi-bot).- Redirect logs to
.logs/<bot-id>.logfor background monitoring.
Confirm startup by checking the log:
tail -f .logs/<bot-id>.logThe app is ready when Server launched at http://0.0.0.0:<PORT> appears.
Use the MCP tools from plugin:slack:slack to interact with the development Slack workspace.
If the plugin:slack:slack MCP tools cannot be found, the likely causes are:
- Authentication for the
plugin:slack:slackplugin has not been completed. - The
plugin:slack:slackplugin has not been set up properly.
Wait about 10 seconds and check again. If the tools are still unavailable, ask the user to install the plugin:slack:slack plugin in Claude Code.
- Send messages:
slack_send_message - Search channels:
slack_search_channels - Search messages:
slack_search_public
After debugging is complete, stop the background processes by PID obtained at startup:
kill <ngrok-pid> <app-pid>Note the PIDs when launching (they are printed after the & command) and use them to stop only the intended processes.
npm run dev uses ts-node-dev, which automatically detects file changes and restarts the server without any manual intervention. When a .ts file is saved, the output looks like:
[INFO] Restarting: /path/to/changed-file.ts has been modified
[INFO] Server launched at http://0.0.0.0:<PORT>
This means you can edit plugin code and immediately test it in Slack without manually restarting the process. Wait for the Server launched line before sending test messages.
- After startup, it may take a few seconds before the first Slack event arrives.
already_reactederrors are expected when a reaction has already been added to a message.
- Follow JavaScript/TypeScript best practices and idiomatic patterns
- Maintain existing code structure and organization
- Write unit tests for new functionality.
- Avoid unnecessary comments; focus on explaining complex logic