ops-jrz1/docs/learner-slack-direct.md
Dan 0ad7ca7b98 Add direct Slack bot path for learners
- learner-add.sh: add users to learners group, source Slack env
- New design doc comparing direct Slack vs maubot/Matrix approach

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 18:56:03 -05:00

5.6 KiB

Direct Slack Bot Development for Learners

Design doc for the "direct to Slack" learner path, bypassing Matrix/maubot.

Overview

Learners 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-learner.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:learners, mode 640
  • Learner users added to learners group on creation
  • .bashrc sources the env file on login

Scripts

/usr/local/bin/learner-add.sh <username> '<ssh-pubkey>'
/usr/local/bin/learner-remove.sh <username> [--archive]

Learner Workflow

1. Get Access

Send SSH pubkey to admin. Admin runs:

ssh root@ops-jrz1 'learner-add.sh alice "ssh-ed25519 AAAA..."'

2. Connect

ssh alice@<server-ip>

Tokens are available immediately:

echo $SLACK_BOT_TOKEN  # xoxb-...
echo $SLACK_APP_TOKEN  # xapp-...

3. Write a Bot

# ~/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

pip install slack-bolt
python bot.py

Bot responds to "hello" and "dice" in Slack.

slack-bolt Basics

Event Handlers

# 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):

@app.command("/mycommand")
def handle_command(ack, respond, command):
    ack()  # Must acknowledge within 3 seconds
    respond(f"You said: {command['text']}")

Sending Messages

# 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 learners use same bot identity
  2. Token exposure - If leaked, affects everyone
  3. Rate limits - Shared across all bots
  4. Process sprawl - N learners = 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 learner manages their own
  2. supervisor - Central process manager
  3. tmux/screen - Manual but simple
  4. Container per learner - 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-Learner Bot Identity

Create separate Slack App per learner:

  • More setup friction
  • Full isolation
  • Each bot has own name/avatar

Test Channel

Create #learner-sandbox in Slack:

  • All learner bots invited
  • Safe place to spam
  • Doesn't pollute real channels

References