Back to the catalog

claude-code-el-slack

Slack event source for the el (event-listeners) plugin. Listens for real Slack messages via a one-shot webhook server with high watermark de

Open source Open in the app JSON README (API)

About

Slack event source for the el (event-listeners) plugin. Listens for real Slack messages via a one-shot webhook server with high watermark deduplication, bot filtering, edit detection, and @mention handling. Enables Claude Code agents to respond to Slack channel activity in real time without polling. Pairs with ngrok for public webhook URLs.

Details

Kind
Plugins
Topic
Communication
Publisher
mividtim
Origin
marketplace
Category
ferramentas
Last push
2026-02-28T14:50:47Z
Repository state
ativo
Language
Python
License
MIT
Added
2026-08-30 01:48:58
Updated
2026-08-30 01:48:58
Origin id
mividtim/claude-code-el-slack/claude-code-el-slack

README

# claude-code-el-slack

Community event source for [claude-code-event-listeners](https://github.com/mividtim/claude-code-event-listeners) that listens for Slack messages via webhook.

## Install

```bash
# From the marketplace (recommended — auto-discovers, pulls in el as dependency)
claude plugin marketplace add mividtim/claude-code-el-slack
claude plugin install el-slack

# Or manually register the source
git clone https://github.com/mividtim/claude-code-el-slack.git
/el:register ./claude-code-el-slack/sources.d/slack.sh
```

## Prerequisites

- **Slack app** with Event Subscriptions enabled (message and app_mention events)
- **el-sidecar** running (ships with the el core plugin)
- **ngrok** (or similar) forwarding to the sidecar port (default: 9999)
- Your Slack app's Request URL pointed at `<ngrok-url>/slack`

## Architecture

`slack.sh` runs `slack-processor.py`, which drains raw webhook events from
el-sidecar (the core el plugin's generic HTTP event buffer). The processor
handles all Slack-specific logic: signature verification, event filtering,
watermark dedup, and optional `conversations.history` polling.

```
ngrok tunnel --> el-sidecar (POST /slack, buffers raw body+headers in SQLite)
                      |
                      v
                 slack-processor.py (drains ?source=slack, verifies, filters)
                      |
                      v
                 stdout JSONL --> el plugin
```

The sidecar is source-agnostic — it stores raw requests and tags them by URL
path. The processor is the Slack specialist. This means multiple event sources
(Slack, voice, GitHub, etc.) share one sidecar on one port.

### Legacy mode

The original one-shot listener (`slack-listener.py`) is still included as a fallback:

```bash
python3 sources.d/slack-listener.py [port] [bot-id]
```

## Usage

```
/el:listen slack
```

Or with explicit port and bot ID:

```
/el:listen slack 9999 B0ADWQ06NSV
```

## Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `SLACK_SIGNING_SECRET` | *(none)* | HMAC signing secret for webhook signature verification |
| `SLACK_BOT_ID` | *(none)* | Your bot's ID — messages from this bot are filtered out to prevent self-loops |
| `SLACK_TOKEN` | *(none)* | Bot token for optional `conversations.history` polling |
| `SLACK_CHANNEL` | *(none)* | Channel ID for optional `conversations.history` polling |
| `SIDECAR_URL` | `http://localhost:9999` | el-sidecar base URL |
| `SLACK_WATERMARK_FILE` | `/tmp/el-slack-agent-watermark` | File storing the last processed event timestamp |
| `SLACK_SEEN_IDS_FILE` | `/tmp/slack-webhook-seen-ids` | File storing recently seen client message IDs for dedup |
| `SLACK_POLL_INTERVAL` | `60` | Seconds between `conversations.history` polls (0 = disabled) |

## Output Format

```json
{"user": "U011EBY6S6A", "text": "Hello Herald", "ts": "1770668053.732849", "channel": "C0ADW24BYJV", "type": "message"}
```

Thread replies include `thread_ts`. Messages from other bots include `bot_id`.

When multiple messages are buffered, each is output on its own line (JSONL format).

## What it handles

| Concern | How |
|---------|-----|
| **Slack URL verification** | el-sidecar echoes the challenge automatically |
| **Signature verification** | HMAC-SHA256 verification of `X-Slack-Signature` (optional, requires `SLACK_SIGNING_SECRET`) |
| **Bot self-filtering** | Skips messages from your own bot ID (configurable) |
| **Watermark dedup** | Skips events older than the last processed timestamp |
| **@mention double-delivery** | Slack sends both `message` and `app_mention` for @mentions — deduplicates via `client_msg_id` |
| **Message edit filtering** | Skips `message_changed` and `message_deleted` subtypes |
| **Missed message recovery** | Optional `conversations.history` polling catches webhook gaps |

## Typical workflow

```
Slack webhook --> el-sidecar (POST /slack) --> SQLite buffer
                                                    |
slack-processor.py (GET /events?source=slack) <-----+
         |
         v
    stdout JSONL --> el plugin --> agent processes events
```

The sidecar buffers raw webhooks persistently. The processor drains and
filters them. The watermark file ensures you never reprocess old events.

## Requirements

- [claude-code-event-listeners](https://github.com/mividtim/claude-code-event-listeners) plugin installed (v0.8.0+ with el-sidecar)
- Python 3
- ngrok or similar tunnel (forwarding to el-sidecar port)

## License

MIT

More