ops-jrz1/docs/dev-slack-direct.md
Dan bc81b4ec15 Rename learner to dev across codebase
- scripts/learner-*.sh → scripts/dev-*.sh
- docs/learner-*.md → docs/dev-*.md
- tests/test-learner-env.sh → tests/test-dev-env.sh
- Update all internal references

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 10:42:34 -08:00

220 lines
5.6 KiB
Markdown

# Direct Slack Bot Development for Devs
Design doc for the "direct to Slack" dev path, bypassing Matrix/maubot.
## Overview
Devs write Python bots using `slack-bolt` that connect directly to Slack via Socket Mode. No Matrix, no bridge, no maubot.
```
┌─────────────────────────────────────────┐
│ VPS (ops-jrz1) │
│ │
│ alice's bot.py ──┐ │
│ bob's bot.py ────┼── Socket Mode ─────────► Slack API
│ carol's bot.py ──┘ (WebSocket) │
│ │
└─────────────────────────────────────────┘
```
## Current Setup
### Credentials
Shared Slack App tokens stored in `/etc/slack-dev.env`:
| Variable | Purpose |
|----------|---------|
| `SLACK_BOT_TOKEN` | Bot identity (xoxb-...) |
| `SLACK_APP_TOKEN` | Socket Mode connection (xapp-...) |
These come from the existing mautrix-slack bridge login (Chochacho workspace, vlad's account).
### Access Control
- File owned by `root:devs`, mode `640`
- Dev users added to `devs` group on creation
- `.bashrc` sources the env file on login
### Scripts
```
/usr/local/bin/dev-add.sh <username> '<ssh-pubkey>'
/usr/local/bin/dev-remove.sh <username> [--archive]
```
## Dev Workflow
### 1. Get Access
Send SSH pubkey to admin. Admin runs:
```bash
ssh root@ops-jrz1 'dev-add.sh alice "ssh-ed25519 AAAA..."'
```
### 2. Connect
```bash
ssh alice@<server-ip>
```
Tokens are available immediately:
```bash
echo $SLACK_BOT_TOKEN # xoxb-...
echo $SLACK_APP_TOKEN # xapp-...
```
### 3. Write a Bot
```python
# ~/bot.py
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
import os
app = App(token=os.environ["SLACK_BOT_TOKEN"])
@app.message("hello")
def hello(message, say):
say(f"Hey <@{message['user']}>!")
@app.message("dice")
def dice(message, say):
import random
say(f"🎲 You rolled a {random.randint(1, 6)}!")
if __name__ == "__main__":
print("Bot starting...")
SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start()
```
### 4. Run It
```bash
pip install slack-bolt
python bot.py
```
Bot responds to "hello" and "dice" in Slack.
## slack-bolt Basics
### Event Handlers
```python
# Match exact text
@app.message("hello")
def handle_hello(message, say):
say("Hello!")
# Match regex
@app.message(re.compile(r"^roll (\d+)d(\d+)"))
def handle_roll(message, say, context):
# context["matches"] contains regex groups
pass
# Match any message
@app.event("message")
def handle_all(event, say):
pass
# React to app mentions (@bot)
@app.event("app_mention")
def handle_mention(event, say):
say("You mentioned me!")
```
### Slash Commands
Requires additional Slack App config (not set up yet):
```python
@app.command("/mycommand")
def handle_command(ack, respond, command):
ack() # Must acknowledge within 3 seconds
respond(f"You said: {command['text']}")
```
### Sending Messages
```python
# Reply in thread
say("Reply!", thread_ts=message["ts"])
# Send to specific channel
app.client.chat_postMessage(channel="C1234567", text="Hello")
# Rich formatting (blocks)
say(blocks=[
{"type": "section", "text": {"type": "mrkdwn", "text": "*Bold* and _italic_"}}
])
```
## Tradeoffs
### vs Maubot/Matrix Path
| Aspect | Direct Slack | Maubot/Matrix |
|--------|--------------|---------------|
| **Setup complexity** | Lower - just Python | Higher - Matrix + bridge + maubot |
| **Feedback loop** | Fast - direct to Slack | Slower - goes through bridge |
| **Mental model** | Simple - just Slack | Complex - Matrix concepts |
| **Documentation** | Excellent (Bolt is popular) | Sparse (maubot is niche) |
| **Process management** | Manual (you run it) | Managed (maubot lifecycle) |
| **Bot identity** | Shared (all bots = same user) | Per-instance configurable |
| **Crash recovery** | Manual restart | Auto-managed |
| **Production path** | Direct - same code works | Need to rewrite for Slack-only |
### Risks
1. **Shared tokens** - All devs use same bot identity
2. **Token exposure** - If leaked, affects everyone
3. **Rate limits** - Shared across all bots
4. **Process sprawl** - N devs = N processes to manage
5. **No guardrails** - Bad code can spam/crash easily
### Mitigations
- Dedicated test channel in Slack
- Supervisor/systemd for process management (not yet set up)
- Tokens in env vars, not code
- Can rotate tokens if leaked (re-login bridge)
## Future Improvements
### Process Management
Options:
1. **systemd user services** - Each dev manages their own
2. **supervisor** - Central process manager
3. **tmux/screen** - Manual but simple
4. **Container per dev** - Isolation but complex
### Starter Template
Create `~/slack-bot-template/` with:
- `bot.py` - Hello world
- `requirements.txt` - slack-bolt pinned
- `Makefile` - run, install targets
- `README.md` - Quick start
### Per-Dev Bot Identity
Create separate Slack App per dev:
- More setup friction
- Full isolation
- Each bot has own name/avatar
### Test Channel
Create `#dev-sandbox` in Slack:
- All dev bots invited
- Safe place to spam
- Doesn't pollute real channels
## References
- [Bolt for Python - Getting Started](https://slack.dev/bolt-python/tutorial/getting-started)
- [Slack API - Socket Mode](https://api.slack.com/apis/connections/socket)
- [Slack API - Events](https://api.slack.com/events)
- [slack-bolt GitHub](https://github.com/slackapi/bolt-python)