Skip to content
This repository was archived by the owner on Oct 31, 2025. It is now read-only.

Accompany-VC/zatech-bot-ARCHIVED

Repository files navigation

ZEBRAS πŸ¦“

ZaTech Engagement Bot for Responses, Automation & Support

A modern, modular Python framework for building powerful Slack community bots. Built with async-first architecture, plugin extensibility, and production-ready observability.

✨ Features

  • πŸ”Œ Plugin System - Modular event handlers, slash commands, interactive components
  • πŸš€ Async-First - Built on asyncio with SQLAlchemy async and aiohttp
  • 🎯 Socket Mode - Zero setup for local development (no tunneling required)
  • 🌐 HTTP Events API - Production-ready with signature verification
  • πŸ“Š Postgres + Redis - Persistent storage and background jobs
  • 🎨 Admin UI - Web interface for managing rules and settings
  • πŸ›‘οΈ Safe by Default - Middleware for rate limiting, idempotency, and validation

πŸš€ Quick Start (Docker Only)

Prerequisites

  • Docker Desktop installed and running
  • Slack workspace with admin access

1. Create Slack App

  1. Go to api.slack.com/apps
  2. Click "Create New App" β†’ "From a manifest"
  3. Select your workspace
  4. Copy and paste the contents of manifest.json
  5. Click "Create" and install to workspace

2. Get Tokens

Bot Token:

  • Navigate to OAuth & Permissions
  • Copy Bot User OAuth Token (starts with xoxb-)

App Token (for Socket Mode):

  • Navigate to Basic Information
  • Scroll to App-Level Tokens
  • Click "Generate Token and Scopes"
  • Add connections:write scope
  • Copy the token (starts with xapp-)

Signing Secret (for HTTP Mode):

  • Navigate to Basic Information β†’ App Credentials
  • Copy Signing Secret

3. Configure Environment

Create .env file in the project root:

# Required for Socket Mode
SLACK_BOT_TOKEN=xoxb-your-bot-token-here
SLACK_APP_TOKEN=xapp-your-app-token-here

# Required for HTTP Mode
SLACK_SIGNING_SECRET=your-signing-secret-here

# Optional - defaults work for Docker
DATABASE_URL=postgresql+asyncpg://zebras:zebras@postgres:5432/zebras
REDIS_URL=redis://redis:6379/0
LOG_LEVEL=INFO

4. Start the Bot

Socket Mode (Recommended for Local Dev):

docker compose up --build zebras-socket

The bot will:

  • βœ… Start Postgres and Redis automatically
  • βœ… Run database migrations
  • βœ… Connect to Slack via WebSocket
  • βœ… Listen for events

HTTP Mode (with Admin UI):

docker compose up --build zebras-http

Then open http://localhost:43117 for the admin interface.

5. Test It Out

In your Slack workspace:

Auto-Responder:

/auto add phrase:"hello" reply:"Hi there!" match:contains scope:here

Send "hello" β†’ bot responds "Hi there!" ✨

Channel Rules:

/rules set allow_bots:no allow_top:yes allow_threads:yes
/rules list

Check Logs:

docker compose logs -f zebras-socket

πŸ“¦ What's Included

Core Plugins

Plugin Description
autoresponder Pattern-based automatic replies (global or per-channel)
rules Channel governance (bot restrictions, thread controls, posting rules)
invite Invite request management with admin approvals
logging Event persistence to Postgres for audit trails
admin Web UI for managing settings (HTTP mode only)
debug Development utilities and debugging tools

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Slack   β”‚
β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
     β”‚
     ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Socket / HTTP      β”‚  ← Adapters
β”‚  Event Receiver     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          β”‚
          ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚   Router    β”‚      ← Normalization
    β”‚ + Middlewareβ”‚      ← Rate limiting, idempotency
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
           β”‚
      β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”
      ↓         ↓
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Plugins β”‚ β”‚  Storage β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚ Postgres β”‚
               β”‚  Redis   β”‚
               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ› οΈ Development

Available Commands

Docker (Recommended):

# Socket mode (local dev)
docker compose up --build zebras-socket

# HTTP mode + Admin UI
docker compose up --build zebras-http

# Background worker
docker compose up --build zebras-worker

# View logs
docker compose logs -f zebras-socket

# Stop everything
docker compose down

Python (Direct):

# Install
pip install -e .

# Socket mode
zebras socket

# HTTP mode
zebras http --port 43117

# Worker
zebras worker

# Database migrations
zebras db upgrade
zebras db downgrade base

Project Structure

zatech-bot/
β”œβ”€β”€ src/zebras/
β”‚   β”œβ”€β”€ cli.py              # CLI entrypoint
β”‚   β”œβ”€β”€ router.py           # Event routing
β”‚   β”œβ”€β”€ app_context.py      # Shared app state
β”‚   β”œβ”€β”€ config.py           # Settings schema
β”‚   β”œβ”€β”€ slack/
β”‚   β”‚   β”œβ”€β”€ socket.py       # Socket Mode adapter
β”‚   β”‚   └── http/
β”‚   β”‚       └── app.py      # FastAPI HTTP adapter
β”‚   β”œβ”€β”€ plugin/
β”‚   β”‚   └── registry.py     # Plugin registration
β”‚   β”œβ”€β”€ plugins/            # Core plugins
β”‚   β”‚   β”œβ”€β”€ autoresponder/
β”‚   β”‚   β”œβ”€β”€ rules/
β”‚   β”‚   β”œβ”€β”€ invite/
β”‚   β”‚   β”œβ”€β”€ logging/
β”‚   β”‚   └── admin/
β”‚   β”œβ”€β”€ storage/
β”‚   β”‚   β”œβ”€β”€ models.py       # SQLAlchemy models
β”‚   β”‚   β”œβ”€β”€ datastore.py    # Postgres engine
β”‚   β”‚   └── kv.py          # Redis client
β”‚   └── worker/
β”‚       └── queue.py        # RQ background jobs
β”œβ”€β”€ migrations/             # Alembic migrations
β”œβ”€β”€ docker-compose.yml      # Docker orchestration
β”œβ”€β”€ manifest.json          # Slack app manifest
β”œβ”€β”€ .env                   # Your config (gitignored)
└── README.md             # This file

Creating a Plugin

  1. Create plugin directory:
mkdir -p src/zebras/plugins/myplugin
touch src/zebras/plugins/myplugin/__init__.py
  1. Implement the plugin:
# src/zebras/plugins/myplugin/__init__.py
from zebras.plugin import Registry

def register(reg: Registry):
    @reg.events.on('message')
    async def on_message(payload):
        event = payload.get('event', payload)
        text = event.get('text', '')
        print(f"Message received: {text}")

    @reg.commands.slash('/mycommand')
    async def my_command(payload):
        return {
            "response_type": "ephemeral",
            "text": "Hello from my plugin!"
        }
  1. Register in src/zebras/cli.py:
from .plugins.myplugin import register as myplugin_register

def _load_plugins(reg: Registry):
    # ... existing plugins
    myplugin_register(reg)

Database Migrations

# Create new migration
alembic revision --autogenerate -m "add my_table"

# Apply migrations
zebras db upgrade

# Rollback
zebras db downgrade -1

πŸ”§ Configuration

Environment Variables

Variable Required Default Description
SLACK_BOT_TOKEN βœ… - Bot User OAuth Token (xoxb-...)
SLACK_APP_TOKEN Socket Mode - App-Level Token (xapp-...)
SLACK_SIGNING_SECRET HTTP Mode - Request signature verification
DATABASE_URL No SQLite Postgres DSN (Neon works!)
REDIS_URL No redis://localhost:6379/0 Redis connection string
LOG_LEVEL No INFO DEBUG, INFO, WARNING, ERROR
ZEBRAS_HTTP_HOST No 0.0.0.0 HTTP server bind address
ZEBRAS_HTTP_PORT No 3000 HTTP server port

Using Neon Postgres (Free Serverless)

  1. Create database at neon.tech
  2. Copy the connection string
  3. Add to .env:
DATABASE_URL=postgresql+asyncpg://user:pass@ep-xxx.us-east-2.aws.neon.tech/zebras?sslmode=require

That's it! No code changes needed.

πŸš€ Production Deployment

HTTP Mode (Production-Ready)

  1. Deploy to cloud (Railway, Fly.io, AWS, etc.)

  2. Set environment variables:

    • SLACK_BOT_TOKEN
    • SLACK_SIGNING_SECRET
    • DATABASE_URL (managed Postgres)
    • REDIS_URL (managed Redis)
  3. Configure Slack Event Subscriptions:

    • Go to your Slack App β†’ Event Subscriptions
    • Enable Events
    • Request URL: https://your-domain.com/slack/events
    • Subscribe to: message.channels, team_join, channel_created, etc.
  4. Configure Interactivity:

    • Go to Interactivity & Shortcuts
    • Request URL: https://your-domain.com/slack/interactivity
  5. Deploy:

    docker compose up -d zebras-http zebras-worker

Health Check

curl https://your-domain.com/healthz
# {"status": "ok"}

πŸ§ͺ Testing

Manual Testing

Events Endpoint:

curl -X POST http://localhost:43117/slack/events \
  -H 'Content-Type: application/json' \
  -d '{
    "type": "event_callback",
    "event": {
      "type": "message",
      "user": "U123",
      "channel": "C123",
      "text": "test message"
    }
  }'

Slash Commands:

curl -X POST http://localhost:43117/slack/commands \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data 'command=%2Frules&text=list'

Unit Tests

pytest -q

πŸ› Troubleshooting

Bot not responding?

1. Check Docker containers are running:

docker compose ps

2. View logs:

docker compose logs -f zebras-socket

3. Verify tokens in .env:

  • βœ… SLACK_BOT_TOKEN starts with xoxb-
  • βœ… SLACK_APP_TOKEN starts with xapp-
  • βœ… No extra spaces or quotes

4. Check Slack app installation:

  • App must be installed to workspace
  • Bot must be invited to channels (/invite @YourBot)

Enable Debug Logging

In docker-compose.yml or .env:

LOG_LEVEL=DEBUG

Then restart:

docker compose restart zebras-socket

Look for detailed logs:

docker compose logs zebras-socket | grep -i autoresponder

Database Issues

Reset everything:

docker compose down -v
docker compose up --build zebras-socket

Common Errors

Error Solution
SLACK_APP_TOKEN required Add SLACK_APP_TOKEN to .env
connection refused postgres Run docker compose up postgres -d
Rule unknown missing attributes Restart after code changes: docker compose restart
Events not triggering Check bot is in channel: /invite @YourBot

πŸ“š Learning More

Key Files to Read

  1. src/zebras/cli.py - CLI commands and plugin loading
  2. src/zebras/router.py - Event routing and middleware
  3. src/zebras/plugins/autoresponder/ - Example plugin implementation
  4. manifest.json - Slack app permissions and settings

Example Use Cases

Auto-respond to mentions:

@reg.events.on('app_mention')
async def handle_mention(payload):
    event = payload['event']
    channel = event['channel']
    client = await get_context().web_client()
    await client.chat_postMessage(
        channel=channel,
        text="You mentioned me! How can I help?"
    )

Lock threads after 24 hours:

@reg.scheduled.cron('0 * * * *')  # Every hour
async def lock_old_threads():
    # Find threads older than 24h
    # Post "Thread locked" message
    # Update channel rules
    pass

πŸ“ License

MIT License - see LICENSE file

🀝 Contributing

Contributions welcome! Please:

  1. Fork the repo
  2. Create a feature branch (git checkout -b feature/amazing)
  3. Commit changes (git commit -m 'Add amazing feature')
  4. Push to branch (git push origin feature/amazing)
  5. Open a Pull Request

πŸ†˜ Support


Built with ❀️ for the ZaTech community

About

No description, website, or topics provided.

Resources

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages