feat(gateway): add inbound reaction event routing for Discord - #8379
Open
yshen92 wants to merge 8 commits into
Open
feat(gateway): add inbound reaction event routing for Discord#8379yshen92 wants to merge 8 commits into
yshen92 wants to merge 8 commits into
Conversation
yshen92
force-pushed
the
feat/discord-reaction-events
branch
from
April 12, 2026 13:52
595ab49 to
f47aa86
Compare
yshen92
force-pushed
the
feat/discord-reaction-events
branch
from
May 1, 2026 10:48
5620a87 to
e9a9e68
Compare
yshen92
force-pushed
the
feat/discord-reaction-events
branch
from
May 31, 2026 02:21
e9a9e68 to
97e788b
Compare
Port the Feishu adapter's synthetic reaction event pattern to Discord.
Users reacting on bot messages are now routed as 'reaction:{action}:{emoji}'
synthetic text events through the normal message pipeline.
- Add _handle_inbound_reaction() method to DiscordAdapter
- Add build_session_key import from gateway.session
- Filter: bot's own reactions, disallowed users, non-bot messages
- Use handle_message() for proper session lifecycle management
- Match Feishu's reaction:{action}:{emoji} synthetic text format
Add Discord event listeners for reaction add/remove alongside existing on_ready, on_message, and on_voice_state_update handlers in connect().
Cover the full _handle_inbound_reaction code path: - Happy path routing as synthetic text event - Non-bot messages ignored - Bot's own reactions ignored (loop prevention) - Disallowed users ignored - DISCORD_REACTIONS=false blocks inbound routing - Reaction removal uses 'removed' action - Uncached channel fetched via fetch_channel - Guild context resolves chat_name with server name - Member display_name used as user_name - Channel fetch failure handled gracefully
…ging Critical: - Fix feedback loop: pass payload as raw_message instead of fetched Message to prevent on_processing_start/complete from adding 👀/✅/❌ on bot messages Important: - Remove dead build_session_key import (already imported locally where needed) - Add dedup guard using composite key for Discord RESUME replay protection - Use logger.warning instead of logger.debug for operational errors - Use composite message_id for unique event identification Minor: - Add _client/_client.user null guard for post-disconnect race condition - Add comment documenting reaction intents in Intents.default() - Fix magic number in test_inbound_reaction_from_bot_ignored Tests: - Add test for feedback loop prevention (raw_message is payload, not Message) - Add test for DM fallback user_name when member is None - Add test for _client=None graceful handling
…reading - message_id stays as str(payload.message_id) for valid Discord snowflake used by send() for reply_to resolution via int() - dedup_key remains separate for _dedup.is_duplicate() reconnect protection - Add test_inbound_reaction_duplicate_suppressed for dedup coverage
yshen92
force-pushed
the
feat/discord-reaction-events
branch
from
June 20, 2026 11:04
97e788b to
b87a2d3
Compare
|
Would love to see this feature implemented! Currently setting up a Discord bot and want to establish more complex workflows where a cron runs and I can react to it to continue the workflow. |
teknium1
reviewed
Jul 12, 2026
teknium1
left a comment
Contributor
There was a problem hiding this comment.
Thanks for implementing a real missing Discord capability: current main has outbound lifecycle reactions but no inbound raw-reaction listener.
Problems
plugins/platforms/discord/adapter.py:1699calls_is_allowed_user()with only an ID. Current main’s normal message path supplies member, guild/DM, and channel context (plugins/platforms/discord/adapter.py:1157-1163); without it, role-only and channel-only authorization cannot admit reactions. The synthetic source also lacks the role-authorized signal consumed bygateway/authz_mixin.py:431-437.plugins/platforms/discord/adapter.py:1749dispatches directly tohandle_message(), bypassing the normalDISCORD_ALLOWED_CHANNELSandDISCORD_IGNORED_CHANNELSchecks atplugins/platforms/discord/adapter.py:6172-6185.
Suggested changes
- Resolve the channel/member/guild first, apply the same channel and authorization gates as normal Discord input, and preserve role authorization on the constructed source.
- Add tests for role-only access plus allowed/ignored channel policy parity.
Automated hermes-sweeper review.
Author
|
Hi @teknium1, thanks for the review. I have addressed them accordingly. Feel free to let me know if there're any changes needed. |
…-events # Conflicts: # plugins/platforms/discord/adapter.py # tests/gateway/test_discord_reactions.py
1 task
# Conflicts: # plugins/platforms/discord/adapter.py
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.
What does this PR do?
Adds inbound reaction event handling to the Discord adapter, porting the proven "synthetic reaction event" pattern from the Feishu adapter. Users can now interact with Hermes via emoji reactions on Discord — reacting on a bot message routes a
reaction:{action}:{emoji}synthetic text event through the normal message pipeline.Currently the Discord adapter can send reactions (👀, ✅, ❌) but has no listener to read incoming user reactions. This closes that gap and brings Discord to feature parity with Feishu.
Related Issue
N/A — identified during user feature audit.
Type of Change
Changes Made
gateway/platforms/discord.py:_handle_inbound_reaction()method — filters bot-self reactions, disallowed users, non-bot messages; builds syntheticMessageEventwithreaction:{action}:{emoji}text; routes viahandle_message()on_raw_reaction_addandon_raw_reaction_removeevent handlers inconnect()_client/_client.usernull guard for post-disconnect raceTYPE_CHECKINGimport forRawReactionActionEventtype hintLiteral["added", "removed"]typing onactionparameterIntents.default()tests/gateway/test_discord_reactions.py(14 new tests):DISCORD_REACTIONS=falseblocks inbound routingfetch_channelraw_message=payloadnotMessage)_client=Nonegraceful handlingHow to Test
pytest tests/gateway/test_discord_reactions.py -vreaction:added:👍and processes itreaction:removed:👍Checklist
Code
Documentation & Housekeeping
Screenshots / Logs
Full
test_discord_reactions.pysuite (21 tests, 0 failures):