Initial commit
This commit is contained in:
12
.claude-plugin/plugin.json
Normal file
12
.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "searching-slack-history",
|
||||||
|
"description": "Search Slack message history using Web API - provides patterns for search.messages and conversations.history endpoints",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"author": {
|
||||||
|
"name": "Ralph Bean",
|
||||||
|
"email": "rbean@redhat.com"
|
||||||
|
},
|
||||||
|
"skills": [
|
||||||
|
"./skills"
|
||||||
|
]
|
||||||
|
}
|
||||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# searching-slack-history
|
||||||
|
|
||||||
|
Search Slack message history using Web API - provides patterns for search.messages and conversations.history endpoints
|
||||||
45
plugin.lock.json
Normal file
45
plugin.lock.json
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"$schema": "internal://schemas/plugin.lock.v1.json",
|
||||||
|
"pluginId": "gh:ralphbean/claude-code-plugins:plugins/searching-slack-history",
|
||||||
|
"normalized": {
|
||||||
|
"repo": null,
|
||||||
|
"ref": "refs/tags/v20251128.0",
|
||||||
|
"commit": "8edf2013d2a49f4cd8f4fdb8e319b9e4f97b91b9",
|
||||||
|
"treeHash": "fff73785a07b873a26ff5ae6ebed6ba50333ed06df8f90eec50f12b7a5c7ee58",
|
||||||
|
"generatedAt": "2025-11-28T10:27:47.109935Z",
|
||||||
|
"toolVersion": "publish_plugins.py@0.2.0"
|
||||||
|
},
|
||||||
|
"origin": {
|
||||||
|
"remote": "git@github.com:zhongweili/42plugin-data.git",
|
||||||
|
"branch": "master",
|
||||||
|
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
|
||||||
|
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
|
||||||
|
},
|
||||||
|
"manifest": {
|
||||||
|
"name": "searching-slack-history",
|
||||||
|
"description": "Search Slack message history using Web API - provides patterns for search.messages and conversations.history endpoints",
|
||||||
|
"version": "0.1.0"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "README.md",
|
||||||
|
"sha256": "08d60c7940a9725ca2362052fcac206bfb592fa6c5a0ef2724c45acf61e5e14b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": ".claude-plugin/plugin.json",
|
||||||
|
"sha256": "601170930077cbf9479392f22f043d2b968fbb039d307a54f0b93b7174491c73"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/searching-slack-history/SKILL.md",
|
||||||
|
"sha256": "a676236dc56620da11cfe77529cc6b79472c4235e4ed6ae20fa52fc6c32c2cf9"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dirSha256": "fff73785a07b873a26ff5ae6ebed6ba50333ed06df8f90eec50f12b7a5c7ee58"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"scannedAt": null,
|
||||||
|
"scannerVersion": null,
|
||||||
|
"flags": []
|
||||||
|
}
|
||||||
|
}
|
||||||
304
skills/searching-slack-history/SKILL.md
Normal file
304
skills/searching-slack-history/SKILL.md
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
---
|
||||||
|
name: searching-slack-history
|
||||||
|
description: Use when needing to search Slack message history, find past conversations, or retrieve channel messages - provides Slack Web API patterns for search.messages and conversations.history endpoints using WebFetch or curl
|
||||||
|
---
|
||||||
|
|
||||||
|
# Searching Slack History
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Search Slack message history using the Slack Web API.
|
||||||
|
|
||||||
|
**Core principle:** Use Slack's REST API endpoints with WebFetch or curl to search messages, retrieve channel history, and find past conversations.
|
||||||
|
|
||||||
|
**Prerequisites:** Slack Bot token with appropriate scopes (setup guide below)
|
||||||
|
|
||||||
|
## Setup Guide (One-Time)
|
||||||
|
|
||||||
|
### Creating a Slack Bot Token
|
||||||
|
|
||||||
|
1. Visit https://api.slack.com/apps → Click "Create New App"
|
||||||
|
2. Choose "From scratch"
|
||||||
|
3. Name it (e.g., "Claude Search Bot")
|
||||||
|
4. Select your workspace
|
||||||
|
5. Navigate to "OAuth & Permissions" in sidebar
|
||||||
|
6. Scroll to "Scopes" → "Bot Token Scopes"
|
||||||
|
7. Add these scopes:
|
||||||
|
- `search:read` - Search messages across workspace
|
||||||
|
- `channels:history` - Read public channel history
|
||||||
|
- `channels:read` - List public channels
|
||||||
|
- `groups:history` - Read private channel history (if needed)
|
||||||
|
- `groups:read` - List private channels (if needed)
|
||||||
|
8. Click "Install to Workspace" at top of page
|
||||||
|
9. Authorize the app
|
||||||
|
10. Copy the "Bot User OAuth Token" (starts with `xoxb-`)
|
||||||
|
11. Store securely as environment variable:
|
||||||
|
```bash
|
||||||
|
export SLACK_TOKEN="xoxb-your-token-here"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Security:** Never commit tokens to git. Use environment variables or secure credential storage.
|
||||||
|
|
||||||
|
**Private channels:** Bot must be invited to private channels before it can search them (`/invite @YourBotName`).
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
| Task | Endpoint | Key Parameters |
|
||||||
|
|------|----------|----------------|
|
||||||
|
| Search all messages | `search.messages` | `query`, `sort`, `count` |
|
||||||
|
| Get channel history | `conversations.history` | `channel`, `limit`, `oldest` |
|
||||||
|
| Find channel ID | `conversations.list` | `types`, `exclude_archived` |
|
||||||
|
| Search by date range | `search.messages` | `query` with date operators |
|
||||||
|
|
||||||
|
## API Patterns
|
||||||
|
|
||||||
|
**Security Note:** Never echo commands containing tokens. Use silent execution to prevent token exposure in terminal output.
|
||||||
|
|
||||||
|
### Search Messages Globally
|
||||||
|
|
||||||
|
**Endpoint:** `POST https://slack.com/api/search.messages`
|
||||||
|
|
||||||
|
**Use case:** Find messages across all channels the bot has access to.
|
||||||
|
|
||||||
|
**Secure pattern (use this):**
|
||||||
|
```bash
|
||||||
|
# Create curl command without echoing it
|
||||||
|
cat > /tmp/slack_search.sh << 'SCRIPT'
|
||||||
|
curl -s -X POST https://slack.com/api/search.messages \
|
||||||
|
-H "Authorization: Bearer $SLACK_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"query": "your search terms",
|
||||||
|
"count": 20,
|
||||||
|
"sort": "timestamp"
|
||||||
|
}'
|
||||||
|
SCRIPT
|
||||||
|
|
||||||
|
# Execute without showing token in terminal
|
||||||
|
bash /tmp/slack_search.sh
|
||||||
|
rm /tmp/slack_search.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Alternative secure pattern:**
|
||||||
|
```bash
|
||||||
|
# Use curl config file (doesn't appear in terminal/history)
|
||||||
|
cat > /tmp/slack.curl << EOF
|
||||||
|
header = "Authorization: Bearer $SLACK_TOKEN"
|
||||||
|
header = "Content-Type: application/json"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
curl -s -K /tmp/slack.curl -X POST https://slack.com/api/search.messages \
|
||||||
|
-d '{"query": "your search terms", "count": 20, "sort": "timestamp"}'
|
||||||
|
|
||||||
|
rm /tmp/slack.curl
|
||||||
|
```
|
||||||
|
|
||||||
|
**Query operators:**
|
||||||
|
- `"exact phrase"` - Match exact phrase
|
||||||
|
- `from:@username` - Messages from specific user
|
||||||
|
- `in:#channel` - Messages in specific channel
|
||||||
|
- `after:YYYY-MM-DD` - Messages after date
|
||||||
|
- `before:YYYY-MM-DD` - Messages before date
|
||||||
|
|
||||||
|
**Example with operators:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"query": "deployment after:2025-01-01 in:#engineering",
|
||||||
|
"count": 50
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rate limit:** Tier 3 (20 requests/minute)
|
||||||
|
|
||||||
|
### Get Channel History
|
||||||
|
|
||||||
|
**Endpoint:** `POST https://slack.com/api/conversations.history`
|
||||||
|
|
||||||
|
**Use case:** Retrieve recent messages from a specific channel.
|
||||||
|
|
||||||
|
**Important:** Requires channel ID, not channel name. Use `conversations.list` first if you only have the name.
|
||||||
|
|
||||||
|
**Secure pattern:**
|
||||||
|
```bash
|
||||||
|
# Use config file to hide token
|
||||||
|
cat > /tmp/slack.curl << EOF
|
||||||
|
header = "Authorization: Bearer $SLACK_TOKEN"
|
||||||
|
header = "Content-Type: application/json"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
curl -s -K /tmp/slack.curl -X POST https://slack.com/api/conversations.history \
|
||||||
|
-d '{"channel": "C1234567890", "limit": 100}'
|
||||||
|
|
||||||
|
rm /tmp/slack.curl
|
||||||
|
```
|
||||||
|
|
||||||
|
**Time filtering:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"channel": "C1234567890",
|
||||||
|
"oldest": "1609459200.000000",
|
||||||
|
"latest": "1640995200.000000",
|
||||||
|
"limit": 100
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Timestamps are Unix epoch time with microseconds (e.g., `1609459200.000000` = 2021-01-01).
|
||||||
|
|
||||||
|
### Find Channel ID from Name
|
||||||
|
|
||||||
|
**Endpoint:** `POST https://slack.com/api/conversations.list`
|
||||||
|
|
||||||
|
**Use case:** Get channel ID when you only know the channel name.
|
||||||
|
|
||||||
|
**Secure pattern:**
|
||||||
|
```bash
|
||||||
|
cat > /tmp/slack.curl << EOF
|
||||||
|
header = "Authorization: Bearer $SLACK_TOKEN"
|
||||||
|
header = "Content-Type: application/json"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
curl -s -K /tmp/slack.curl -X POST https://slack.com/api/conversations.list \
|
||||||
|
-d '{"types": "public_channel,private_channel", "exclude_archived": true}'
|
||||||
|
|
||||||
|
rm /tmp/slack.curl
|
||||||
|
```
|
||||||
|
|
||||||
|
**Filter response:** Look for channel with matching `name` field, extract `id`.
|
||||||
|
|
||||||
|
### Handling Pagination
|
||||||
|
|
||||||
|
All endpoints return paginated results. Check for `response_metadata.next_cursor`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create reusable config (do this once)
|
||||||
|
cat > /tmp/slack.curl << EOF
|
||||||
|
header = "Authorization: Bearer $SLACK_TOKEN"
|
||||||
|
header = "Content-Type: application/json"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# First request
|
||||||
|
curl -s -K /tmp/slack.curl -X POST https://slack.com/api/search.messages \
|
||||||
|
-d '{"query": "search", "cursor": ""}'
|
||||||
|
|
||||||
|
# If response contains "next_cursor": "dXNlcjpVMDYxTkZUVDI="
|
||||||
|
# Second request with cursor
|
||||||
|
curl -s -K /tmp/slack.curl -X POST https://slack.com/api/search.messages \
|
||||||
|
-d '{"query": "search", "cursor": "dXNlcjpVMDYxTkZUVDI="}'
|
||||||
|
|
||||||
|
# Clean up when done
|
||||||
|
rm /tmp/slack.curl
|
||||||
|
```
|
||||||
|
|
||||||
|
Continue until `next_cursor` is empty or not present.
|
||||||
|
|
||||||
|
## Common Mistakes
|
||||||
|
|
||||||
|
### 1. Missing Token Scopes
|
||||||
|
**Symptom:** `{"ok": false, "error": "missing_scope"}`
|
||||||
|
|
||||||
|
**Fix:** Add required scopes in Slack App OAuth settings, then reinstall app to workspace.
|
||||||
|
|
||||||
|
### 2. Using Channel Names Instead of IDs
|
||||||
|
**Symptom:** `{"ok": false, "error": "channel_not_found"}`
|
||||||
|
|
||||||
|
**Fix:** Call `conversations.list` first to map channel name → ID.
|
||||||
|
|
||||||
|
### 3. Ignoring Pagination
|
||||||
|
**Symptom:** Only getting first 20-100 results, missing older messages.
|
||||||
|
|
||||||
|
**Fix:** Check for `response_metadata.next_cursor` and make subsequent requests with cursor parameter.
|
||||||
|
|
||||||
|
### 4. Rate Limit Errors
|
||||||
|
**Symptom:** `{"ok": false, "error": "rate_limited"}` or HTTP 429
|
||||||
|
|
||||||
|
**Fix:** Implement exponential backoff. Wait time often provided in `Retry-After` header.
|
||||||
|
|
||||||
|
### 5. Private Channel Access
|
||||||
|
**Symptom:** Empty results or permission errors for private channels.
|
||||||
|
|
||||||
|
**Fix:**
|
||||||
|
- Add `groups:history` and `groups:read` scopes
|
||||||
|
- Invite bot to private channel: `/invite @YourBotName`
|
||||||
|
|
||||||
|
### 6. Token Exposure in Terminal
|
||||||
|
**Risk:** Running curl with `-H "Authorization: Bearer $SLACK_TOKEN"` displays token in terminal output and shell history.
|
||||||
|
|
||||||
|
**Fix:**
|
||||||
|
- **ALWAYS** use curl config files (`-K /tmp/slack.curl`) to hide token
|
||||||
|
- Create config file with headers, run curl referencing it, delete config after
|
||||||
|
- Never echo commands containing `$SLACK_TOKEN`
|
||||||
|
- Add `.env` to `.gitignore` if storing tokens in files
|
||||||
|
- Rotate token immediately if exposed in logs or history
|
||||||
|
|
||||||
|
## Response Format
|
||||||
|
|
||||||
|
Successful responses have `"ok": true`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ok": true,
|
||||||
|
"messages": {
|
||||||
|
"matches": [
|
||||||
|
{
|
||||||
|
"type": "message",
|
||||||
|
"user": "U1234567890",
|
||||||
|
"text": "Message content",
|
||||||
|
"ts": "1234567890.123456",
|
||||||
|
"permalink": "https://workspace.slack.com/archives/...",
|
||||||
|
"channel": {
|
||||||
|
"id": "C1234567890",
|
||||||
|
"name": "general"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 150
|
||||||
|
},
|
||||||
|
"response_metadata": {
|
||||||
|
"next_cursor": "dXNlcjpVMDYxTkZUVDI="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Error responses have `"ok": false`:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ok": false,
|
||||||
|
"error": "invalid_auth"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Real-World Workflow
|
||||||
|
|
||||||
|
**Scenario:** Find all messages about "deployment" in #engineering from last week.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Create secure config file
|
||||||
|
cat > /tmp/slack.curl << EOF
|
||||||
|
header = "Authorization: Bearer $SLACK_TOKEN"
|
||||||
|
header = "Content-Type: application/json"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 2. Get channel ID (pipe to jq to extract)
|
||||||
|
curl -s -K /tmp/slack.curl -X POST https://slack.com/api/conversations.list \
|
||||||
|
-d '{"types": "public_channel"}' \
|
||||||
|
| jq -r '.channels[] | select(.name=="engineering") | .id'
|
||||||
|
# Returns: C1234567890
|
||||||
|
|
||||||
|
# 3. Search messages with date filter
|
||||||
|
curl -s -K /tmp/slack.curl -X POST https://slack.com/api/search.messages \
|
||||||
|
-d '{
|
||||||
|
"query": "deployment in:#engineering after:2025-10-24",
|
||||||
|
"count": 100,
|
||||||
|
"sort": "timestamp"
|
||||||
|
}'
|
||||||
|
|
||||||
|
# 4. Clean up config file
|
||||||
|
rm /tmp/slack.curl
|
||||||
|
```
|
||||||
|
|
||||||
|
## Further Reading
|
||||||
|
|
||||||
|
- Slack Web API Documentation: https://api.slack.com/web
|
||||||
|
- search.messages: https://api.slack.com/methods/search.messages
|
||||||
|
- conversations.history: https://api.slack.com/methods/conversations.history
|
||||||
|
- OAuth Scopes Reference: https://api.slack.com/scopes
|
||||||
Reference in New Issue
Block a user