This repository has no description
README.md
image indigest
a Slack bot that curates messages into digestible RSS feeds & APIs while enabling cross-channel message forwarding via pub-sub style commands

how does this work? #

  1. the bot is added to any channel
  2. a channel manager or bot admin runs /in pub
  3. messages get approved manually by posters ('yep'/'nope' buttons) or automatically (auto-approve) for high-trust channels
  4. approved messages are served via RSS/JSON/Webhooks
  5. if configured, extra metadata can be set through a Slack form modal
  6. managers of other channels can run /in sub to receive forwarded copies of approved messages
wait, but how does it do that?
  • ts
  • bun
  • hono
  • cf workers
  • postgres (hosted on hc infra / orchard)
  • oRPC
  • drizzle orm
  • zod
  • better auth
  • various slack sdks
  • vite
  • react (planning to switch to svelte or preact at some point)
  • react flow

map of the network #

see a node graph mapping the channels currently using indigest in Hack Club at https://indigest.matmanna.dev

image

how do i use this? #

tldr: if you forget what to do, run /in

image

Note

auto-publish will soon only be available to whitelisted "trusted" channels

command description
/in Show help message with all commands
/in pub Enable manual mode (buttons on every message)
/in pub auto Enable auto-approve for all users
/in pub auto @user Auto-publish only that user's messages
/in pub manual Explicitly enable manual mode (clears auto-approve)
/in pub manual @user Only that user's messages get a Yep!/No prompt; others aren't posted
/in unpub Disable indigest for a channel (alias: /in pub off)
/in unpub auto [@user] Remove auto-approve for a user (or all users)
/in sub #channel Subscribe this channel to receive forwarded messages from #channel
/in sub off #channel Unsubscribe this channel from #channel (alias for /in unsub)
/in unsub #channel Unsubscribe this channel from #channel
/in status Show pub status, feed URLs, subscriptions, and permissions
/in webhook <url> Set webhook URL for approved messages
/in webhook clear Remove webhook
/in auto list List auto-approve users
/in schema set <json> Set metadata schema (see below)
/in schema get View current metadata schema
/in schema clear Remove metadata schema
/in schema required on|off Require metadata before forwarding messages

publishing #

manual, per-message, opt-in publishing:

image

permission structure:

  • channel manager - can run all commands unless lockdown mode enabled
  • lockdown override - listed in LOCKDOWN_USERS, can always run all commands
  • channel member - can only view status and public feed URLs
image

subscriptions #

if channel A subscrbies to channel B, when a message is approved in channel B, it is automatically forwarded to channel A as a new Slack message (either posted as a forward or with the original author's name, message, and profile picture.

Note

exceptions to publishing: messages that are empty, contain only a link, or are @here/@channel pings (when recent substantive content exists) are not forwarded.

channel properties & subscriber profile overrides #

a pubbing channel can define static properties (a JSON key/value map), and each subscribing channel can pin its own bot profile picture and username for forwards from a given source. The override is either a literal value/URL, or a [property] that resolves against the source channel's static properties.

# on the pubbing channel: define static properties
/in prop set {"title":"Hack Club","logo":"https://.../logo.png"}

# on the subscribing channel, per source
/in sub pfp #source [logo]        # use the source's "logo" property as the pfp
/in sub name #source [title]      # use the source's "title" property as the username
/in sub name #source "Indigest"   # or a fixed username
/in sub pfp #source clear         # reset to the original author's picture

Unset or unresolved overrides fall back to the original author's name/picture.

setting metadata #

channel managers can define a metadata form that opens in a modal when someone clicks Yep! on a message in manual mode.

image
/in schema set {"title":"Message Metadata","fields":[
  {"action_id":"title","label":"Title","type":"plain_text_input","placeholder":"Enter a title"},
  {"action_id":"description","label":"Description","type":"plain_text_input","multiline":true},
  {"action_id":"priority","label":"Priority","type":"static_select","options":[{"label":"Low","value":"low"},{"label":"High","value":"high"}]},
  {"action_id":"due_date","label":"Due Date","type":"datepicker"}
]}

metadata field types:

Type Notes
plain_text_input Supports multiline, min_length, max_length, placeholder, initial_value
url_text_input URL validation built in
email_text_input Email validation built in
number_input Integer only
static_select Single select, requires options array with {label, value}
multi_static_select Multi-select, requires options array
datepicker Date picker
file_input File upload (uploaded to Hack Club CDN if key is set)

webhooks #

when a message is approved, fires POST to the webhook URL:

{
  "event": "message.approved",
  "channel": { "id": "C123", "name": "general" },
  "message": {
    "ts": "1234567890.123456",
    "user_id": "U456",
    "user_name": "alice",
    "text": "the message text",
    "timestamp": "2025-01-01T00:00:00.000Z",
    "metadata": { "title": "My Title", "priority": "high" }
  }
}

feeds #

  • RSS: https://your-host/feed/{channel_id}
  • JSON: https://your-host/feed/{channel_id}.json?limit=50&offset=0

query parameters: limit (max 200, default 50), offset (default 0).

REST API #

interactive API docs can be found at https://indigest.matmanna.dev/docs.

development #

use either docker or wrangler dev

# Drizzle Studio (browse DB)
docker compose run app bun run db:studio

# Cloudflare Worker
npx wrangler dev        # local dev
npx wrangler deploy     # deploy to Cloudflare

deploy #

npx wrangler login
npx wrangler secret put SLACK_BOT_TOKEN
npx wrangler secret put SLACK_SIGNING_SECRET
npx wrangler secret put DATABASE_URL
npx wrangler deploy

env vars #

see .env.example

horizons ai use #

while blindly vibecoding was avoided, post-initial planning and feature abstraction, ai agents (eg opencode) were used significantly especially for prototyping features, implementing tests, performing database tasks.