Skip to content

Count a thumbs up on the chat bot's message as approval - #169

Merged
tim-smart merged 5 commits into
mainfrom
chat-reaction-approval
Oct 30, 2026
Merged

tim-smart merged 5 commits into
mainfrom
chat-reaction-approval

Conversation

@effect-bot

Copy link
Copy Markdown
Contributor

A 👍 reaction (any skin tone) on one of the chat bot's Slack or Discord messages now counts as approval. It works when the reaction comes from an admitted user, in a thread that has a session. The session gets it as a message from that user, "👍 (reacted with a thumbs up to your message: approved)". That message counts as a mention, so it skips the reply gate and starts or steers a turn. This lets someone answer a yes/no question or an approval request with a thumbs up.

Authorization stays as it is. A reaction never starts a session. Other emoji, reactions on someone else's message, and reactions from outsiders, guests or bots are ignored, not kept as context. Reactions from other guilds and in threads without a session are ignored too.

Discord

  • DiscordIngress.handleReaction handles MESSAGE_REACTION_ADD. The gateway now asks for GUILD_MESSAGE_REACTIONS, which is not privileged, and routes the event through the same before-sequence hook as messages. Approval ids go into SeenMessages, so a RESUME replay is dropped.
  • Admission uses the reaction's member.roles, through a toAuthor helper that is now shared with toChatMessage.
  • Discord sends a Unicode emoji as { id: null, name: "👍" }, although dfx types it as a custom emoji. Matching is by name, and only for emoji with no id.
  • postReply: an approval's id (reaction/{message}/{user}) isn't a Discord snowflake, and Discord would reject a message_reference to it. A turn started by an approval therefore mentions the approver instead of replying to the message, the same way the thread-starter case already works.

Slack

  • The webhook decodes reaction_added. It accepts +1 or thumbsup, with or without ::skin-tone-N, on a message whose item_user is the bot.
  • The event names the message but not its thread, so ingress calls conversations.replies with ts = the reacted message. Per the API docs, ts may be "either a thread's parent message or a message in the thread". Results are ordered earliest first, and every threaded message carries the root's ts as thread_ts, so messages[0].thread_ts is the root. An unthreaded message has no thread_ts and is ignored. A denied error, such as thread_not_found for a deleted message, is logged and the reaction ignored. Other failures answer 503 so Slack retries. The call needs no new scopes: channels:history, groups:history and im:history are already granted.
  • The admission path (session cache, then users.info, then remember) is factored into admit and shared with the message path.

Deployment

  • Slack: both apps need the reactions:read scope and a reaction_added bot event subscription, then a reinstall.
  • Discord: nothing to toggle in the portal; the new intent is picked up when the gateway next IDENTIFYs.

docs/chat.md covers both.

Validation

  • I added one regression test per platform in test/Chat/{Slack,Discord}/Ingress.test.ts. Each covers the approval cases and all the dropped cases. Both fail on main and pass here.
  • vp check passes. All Chat tests pass (95/95).
  • The full backend suite had 12 timeouts in Session and PullRequestReview tests on a heavily loaded runner (load average above 20). The Session tests pass with a 30 s timeout.

Closes EFF-3163

effect-bot and others added 5 commits October 30, 2026 03:30
An admitted user's 👍 on one of the bot's Slack or Discord messages, in a
thread with a session, reaches the session as an approval that counts as
a mention. Other reactions, outsiders' and bots' reactions, and threads
without a session are ignored; a reaction never starts a session.

Closes EFF-3163
Slack decodes only a thumbs up on a message, so the handler loses its shape checks and reuses the author spread. Discord checks the emoji before reading the seen store, and both dispatches share one refusal handler.
@tim-smart
tim-smart merged commit 8878a46 into main Oct 30, 2026
4 checks passed
@tim-smart
tim-smart deleted the chat-reaction-approval branch October 30, 2026 05:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants