{
  "markdown": "# claude-code-el-slack\n\nCommunity event source for [claude-code-event-listeners](https://github.com/mividtim/claude-code-event-listeners) that listens for Slack messages via webhook.\n\n## Install\n\n```bash\n# From the marketplace (recommended — auto-discovers, pulls in el as dependency)\nclaude plugin marketplace add mividtim/claude-code-el-slack\nclaude plugin install el-slack\n\n# Or manually register the source\ngit clone https://github.com/mividtim/claude-code-el-slack.git\n/el:register ./claude-code-el-slack/sources.d/slack.sh\n```\n\n## Prerequisites\n\n- **Slack app** with Event Subscriptions enabled (message and app_mention events)\n- **el-sidecar** running (ships with the el core plugin)\n- **ngrok** (or similar) forwarding to the sidecar port (default: 9999)\n- Your Slack app's Request URL pointed at `<ngrok-url>/slack`\n\n## Architecture\n\n`slack.sh` runs `slack-processor.py`, which drains raw webhook events from\nel-sidecar (the core el plugin's generic HTTP event buffer). The processor\nhandles all Slack-specific logic: signature verification, event filtering,\nwatermark dedup, and optional `conversations.history` polling.\n\n```\nngrok tunnel --> el-sidecar (POST /slack, buffers raw body+headers in SQLite)\n                      |\n                      v\n                 slack-processor.py (drains ?source=slack, verifies, filters)\n                      |\n                      v\n                 stdout JSONL --> el plugin\n```\n\nThe sidecar is source-agnostic — it stores raw requests and tags them by URL\npath. The processor is the Slack specialist. This means multiple event sources\n(Slack, voice, GitHub, etc.) share one sidecar on one port.\n\n### Legacy mode\n\nThe original one-shot listener (`slack-listener.py`) is still included as a fallback:\n\n```bash\npython3 sources.d/slack-listener.py [port] [bot-id]\n```\n\n## Usage\n\n```\n/el:listen slack\n```\n\nOr with explicit port and bot ID:\n\n```\n/el:listen slack 9999 B0ADWQ06NSV\n```\n\n## Environment Variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `SLACK_SIGNING_SECRET` | *(none)* | HMAC signing secret for webhook signature verification |\n| `SLACK_BOT_ID` | *(none)* | Your bot's ID — messages from this bot are filtered out to prevent self-loops |\n| `SLACK_TOKEN` | *(none)* | Bot token for optional `conversations.history` polling |\n| `SLACK_CHANNEL` | *(none)* | Channel ID for optional `conversations.history` polling |\n| `SIDECAR_URL` | `http://localhost:9999` | el-sidecar base URL |\n| `SLACK_WATERMARK_FILE` | `/tmp/el-slack-agent-watermark` | File storing the last processed event timestamp |\n| `SLACK_SEEN_IDS_FILE` | `/tmp/slack-webhook-seen-ids` | File storing recently seen client message IDs for dedup |\n| `SLACK_POLL_INTERVAL` | `60` | Seconds between `conversations.history` polls (0 = disabled) |\n\n## Output Format\n\n```json\n{\"user\": \"U011EBY6S6A\", \"text\": \"Hello Herald\", \"ts\": \"1770668053.732849\", \"channel\": \"C0ADW24BYJV\", \"type\": \"message\"}\n```\n\nThread replies include `thread_ts`. Messages from other bots include `bot_id`.\n\nWhen multiple messages are buffered, each is output on its own line (JSONL format).\n\n## What it handles\n\n| Concern | How |\n|---------|-----|\n| **Slack URL verification** | el-sidecar echoes the challenge automatically |\n| **Signature verification** | HMAC-SHA256 verification of `X-Slack-Signature` (optional, requires `SLACK_SIGNING_SECRET`) |\n| **Bot self-filtering** | Skips messages from your own bot ID (configurable) |\n| **Watermark dedup** | Skips events older than the last processed timestamp |\n| **@mention double-delivery** | Slack sends both `message` and `app_mention` for @mentions — deduplicates via `client_msg_id` |\n| **Message edit filtering** | Skips `message_changed` and `message_deleted` subtypes |\n| **Missed message recovery** | Optional `conversations.history` polling catches webhook gaps |\n\n## Typical workflow\n\n```\nSlack webhook --> el-sidecar (POST /slack) --> SQLite buffer\n                                                    |\nslack-processor.py (GET /events?source=slack) <-----+\n         |\n         v\n    stdout JSONL --> el plugin --> agent processes events\n```\n\nThe sidecar buffers raw webhooks persistently. The processor drains and\nfilters them. The watermark file ensures you never reprocess old events.\n\n## Requirements\n\n- [claude-code-event-listeners](https://github.com/mividtim/claude-code-event-listeners) plugin installed (v0.8.0+ with el-sidecar)\n- Python 3\n- ngrok or similar tunnel (forwarding to el-sidecar port)\n\n## License\n\nMIT\n",
  "bytes": 4516,
  "sha": "d72e441cc002077500ba87562367444803dbe9baec52fa87b2db5a2d7bda1743",
  "repo_slug": "mividtim/claude-code-el-slack",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/plg_mividtim_claude_code_el_slack_claude_cod_856b2385/readme"
}