Post classified submissions to Slack, not just the CMS queue - #187
Merged
Conversation
The Approve/Reject interactivity endpoint has been in place, but nothing ever posted the message those buttons live on: the CMS verified clicks that could not arrive. This adds the outbound half. internal/slack builds the Block Kit notification and posts it to an incoming webhook. The button action_ids and the row id they carry are the contract with internal/handlers/slack.go, so both ends now share exported constants; the handler still accepts the past-tense spellings used by messages already in channel history. The post is deliberately off the request's critical path. It runs in a goroutine on a detached context -- the response is written immediately after, which would otherwise cancel it mid-flight -- and a failure logs rather than 500s. The row is already in the queue, and a reader must not see an error for a moderation channel they do not know exists. Reader-supplied text is escaped and blockquoted so a submission cannot forge links or mimic the CMS's own formatting, and is truncated before Slack's section limit, which would otherwise reject the whole message. The two halves are configured separately and half is worse than none, so startup names whichever variable is missing and the queue UI only promises Slack approvals when both are set. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Approve/Reject interactivity endpoint has been in place for a while, but nothing ever posted the message those buttons live on — the CMS was verifying clicks that could not arrive. This adds the outbound half of the moderation loop.
What's here
server/internal/slack— aNotifierthat renders the Block Kit message (submitter, email, category, run-until date, the message itself, Approve/Reject buttons, and an optional link back to the CMS queue) and posts it to an incoming webhook. The buttonaction_ids and the row id they carry are the contract withinternal/handlers/slack.go, so both ends now share exported constants. The handler still accepts the past-tense spellings used by messages already sitting in channel history.POST /v1/classifiedsnow notifies on success. The post runs in a goroutine on a context detached from the request — the response is written immediately after, which would otherwise cancel it mid-flight — and a failure logs rather than 500s. The row is already in the queue, and a reader must not see an error for a moderation channel they do not know exists.SlackModerationConfigured). A malformed webhook URL is fatal at boot rather than silently failing on every submission.Config
SLACK_WEBHOOK_URL(new, required for notifications) andSLACK_CLASSIFIEDS_QUEUE_URL(new, optional link back to the CMS) added to both compose files and both env examples. Running with neither is still supported — submissions simply wait in the CMS queue.Testing
go build,go vet, andgo test ./...are clean; new tests cover webhook URL validation, the button contract, non-200 error surfacing, escaping, and truncation, plus a table test thatSlackModerationConfiguredrequires both halves. Frontendtsc -bandeslintare clean. Not yet exercised against a real Slack workspace — that needs a webhook URL in the Delta env.🤖 Generated with Claude Code