Commit Graph
2 Commits
Author SHA1 Message Date
siddharthd 9f0f38449b feat(slack): ask the other person too, and put rating before sharing
ci / lint-test (push) Successful in 43s
Two halves of the same requirement, one of which was quietly missing.

Sharing split the money but never reached her: she is not in #smarthome,
so the card whose caption said "both verdicts welcome" was one she could
not see. Now a share DMs her a card of her own. A DM rather than adding
her to the channel, so her surface stays "orders that concern me"
instead of the whole house's ops feed. She was already in SLACK_USER_MAP,
so her press files under participant 4.

Only on the press that turns sharing ON, and only when someone else did
the sharing. Re-notifying on every later rating press would turn one
shared meal into a stream of DMs, which is how a nudge gets muted.

Her card carries no share button: she is being told it was shared, not
asked to decide, and two people toggling one split from separate copies
of a card is a race with no upside.

The app still holds no Slack bot token — it returns a notify instruction
and n8n sends it, the same shape as the modal open. If SLACK_USER_MAP has
no id for her the DM is skipped silently: the split is correct and
complete either way, and failing the press over an unaddressable nudge
would be the worse trade.

Card reordered to rate -> details -> share. You judge the food, then
decide who pays for it; asking "was this shared?" first inverts the
order a person thinks in. The status caption moved under the share button
it describes rather than sitting orphaned mid-card.
2026-07-28 18:33:25 +10:00
siddharthd aaa36dd75e feat(slack): answer the order nudge in Slack, without opening the app
ci / lint-test (push) Successful in 40s
Being sent to a web app to answer "was this shared?" is enough friction
that the question stops getting answered — which is the exact failure the
nudge exists to prevent. So the buttons now act in place: pressing
"Shared 50/50" writes the transaction_splits row and edits the message,
and the app is never opened.

Slack does NOT reach this route directly. It posts to an n8n webhook that
forwards the raw body and signature headers here (user's suggestion).
That is the better shape: n8n already terminates public webhooks, so the
app keeps its blanket OAuth chain and gains no internet-facing
unauthenticated route, and no Traefik change is needed. n8n cannot verify
the signature itself — its Code sandbox has no `require`, so no `crypto`.

Two independent gates, both failing closed: the shared x-ingest-token
(came from n8n) and Slack's v0 signature over the raw body (came from
Slack, not replayed within 5 minutes). An unset signing secret rejects
everything rather than waving it through, because the alternative turns a
misconfigured deploy into an open write endpoint.

An unmapped Slack user is refused rather than defaulted to the owner. In
a two-person household a wrong attribution is not a rounding error, it is
the other person's opinion recorded under your name.

Block Kit is built in the app (ingest returns slack_blocks) rather than
in n8n expressions: a template string is untestable, and this shape has
to stay in step with what the interactive endpoint re-renders after each
press. Null when there is no transaction yet — a card-settled order is
parked until its statement arrives, so there is nothing to split or rate.

Also: /transactions now honours ?q=, so the link lands on the row instead
of the top of an unfiltered ledger.
2026-07-28 15:50:06 +10:00