Skip to content

Repository files navigation

dispatch

PyTexas's communications dispatch service, powered by Temporal. It currently runs two workflows:

  1. Pretix orders: listens for pretix webhook notifications and posts formatted order summaries to a Discord channel.
  2. Monthly meetup dispatch: announces a booked meetup across channels; creates the Discord scheduled event and posts the asset handoff to the marketing channel and a setup summary to the organizers channel. Started from the CLI by the meetup repo's /meetup-update automation.

Pretix Orders

When a new order is placed in pretix, this service:

  1. Receives the webhook POST from pretix
  2. Fetches the full order details from the pretix REST API
  3. Formats a Discord embed with order code, total, buyer email, and purchased products
  4. Sends the embed to a Discord channel via webhook

The pipeline is orchestrated by Temporal, which provides automatic retries and durable execution. If Discord is temporarily unavailable, the notification is delivered when it recovers.

Architecture

flowchart LR
    pretix -- "webhook POST" --> FastAPI
    FastAPI -- "starts workflow" --> Temporal
    Temporal --> fetch["Fetch order from pretix"]
    fetch --> format["Format embed"]
    format --> send["Send to Discord"]
Loading

Services (all run via Docker Compose):

Service Description
caddy Reverse proxy, automatic HTTPS via Let's Encrypt
temporal Temporal dev server with persistent SQLite
worker Temporal worker executing activities
web FastAPI app receiving pretix webhooks

Source layout:

src/pretix_discord/
├── api.py                 # FastAPI app (POST /webhook, GET /health)
├── config.py              # Settings loaded from environment variables
├── discord_activities.py  # Format embed, send Discord webhook
├── main.py                # FastAPI/uvicorn entrypoint
├── models.py              # All dataclasses (orders, embeds, inputs)
├── pretix_activities.py   # Fetch and parse pretix orders
├── worker.py              # Temporal worker entrypoint (registers both workflows)
└── workflow.py            # Workflow: fetch -> format -> send

src/meetup_dispatch/
├── activities.py          # Webhook posts, Discord event creation, message formatting
├── config.py              # Meetup settings loaded from environment variables
├── models.py              # Dispatch input, activity inputs, result
└── workflow.py            # Workflow: create event -> post marketing -> post organizers

Monthly Meetup Dispatch

The meetup workflow is started manually (by the meetup repo's automation) once the month's assets exist:

temporal workflow start \
  --task-queue pretix-discord \
  --type MonthlyMeetupDispatch \
  --workflow-id "meetup-dispatch-2026-08" \
  --input '{"month": "August 2026",
            "date_display": "Tuesday, August 4 at 8:00 PM Central",
            "start_time_utc": "2026-08-05T01:00:00Z",
            "end_time_utc": "2026-08-05T02:00:00Z",
            "talk_title": "...", "speaker_name": "...", "promo_blurb": "...",
            "canva_link": "...", "card_png_link": "...", "run_of_show_link": "...",
            "attendance_form_link": "...", "questions_form_link": "...",
            "still_manual": "Canva page title rename, meetup.com event"}'

Inputs are links and strings only, never file bytes: Temporal caps a payload at 2MB, so the card image is passed as a Drive link. The Discord event is created as an external event with the location text "PyTexas Stage" (stage-linked events have an audio quality bug). If PYTEXAS_DISCORD_BOT_TOKEN is not configured, the event is skipped with a note and both webhook posts still go out with a TBD event link.

Prerequisites

  • A server with Docker installed
  • A domain name pointed at the server (for HTTPS)
  • A pretix API token (docs)
  • A Discord webhook URL (docs)

Configuration

Copy the example environment file and fill in your values:

cp .env.example .env

Required variables

Variable Description
PRETIX_API_TOKEN API token from your pretix organizer account
DISCORD_WEBHOOK_URL Full Discord webhook URL for the pretix order channel
DOMAIN Public domain for this service (used by Caddy for TLS)
PYTEXAS_MARKETING_WEBHOOK Webhook URL for the marketing channel (meetup dispatch)
PYTEXAS_MEETUP_WEBHOOK Webhook URL for the meetup organizers channel (meetup dispatch)

Optional meetup dispatch variables

Variable Default Description
PYTEXAS_DISCORD_BOT_TOKEN unset Bot token with Manage Events; event creation is skipped without it
PYTEXAS_GUILD_ID 1012382914035597372 PyTexas Discord guild ID

Optional variables

Variable Default Description
PRETIX_BASE_URL https://pretix.eu/api/v1 pretix API base URL
TEMPORAL_ADDRESS temporal:7233 Temporal server address
TEMPORAL_NAMESPACE default Temporal namespace
TEMPORAL_TASK_QUEUE pretix-discord Temporal task queue name

Deployment

1. Provision a server

Any VPS with Docker works. On a fresh Ubuntu/Debian server:

curl -fsSL https://get.docker.com | sh

2. Clone and configure

git clone https://github.com/pytexas/dispatch.git
cd dispatch
cp .env.example .env
# Edit .env with your values

3. Point DNS

Create an A record for your domain pointing to the server's IP address. Caddy needs this to provision the TLS certificate.

4. Start the services

docker compose up -d

Caddy will automatically obtain a Let's Encrypt certificate on the first request. The services will restart automatically if the server reboots.

5. Configure pretix

In your pretix organizer settings:

  1. Go to Settings > Webhooks
  2. Add a new webhook with the URL: https://YOUR_DOMAIN/webhook
  3. Select the Order placed event
  4. Save

6. Verify

Check that the service is running:

curl https://YOUR_DOMAIN/health
# {"status":"ok"}

Check the Temporal UI at http://YOUR_SERVER_IP:8233 to monitor workflows.

Local Development

Requirements

Setup

uv sync

Running checks

just check

This runs formatting (ruff), tests (pytest), linting (ruff), and type checking (mypy strict).

Individual targets

just fmt        # Format code
just test       # Run tests
just lint       # Lint code
just typecheck  # Type check (mypy --strict)

Smoke Test

With Docker Compose running locally:

curl -X POST http://localhost:8000/webhook \
  -H "Content-Type: application/json" \
  -d '{
    "notification_id": 99999,
    "organizer": "your-organizer",
    "event": "your-event",
    "code": "XXXXX",
    "action": "pretix.event.order.placed"
  }'

The workflow will appear in the Temporal UI. If the order code is valid and your tokens are configured, a Discord message will appear in the target channel.

Monitoring

  • Health check: GET /health returns {"status": "ok"}
  • Temporal UI: Port 8233 — view workflow history, retries, and failures
  • Docker logs: docker compose logs -f worker to watch activity execution

License

MIT

About

Middleware for translating pretix webhooks into discord compatible webhooks

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages