-
Notifications
You must be signed in to change notification settings - Fork 0
feat: implement custom message routing system #128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: default
Are you sure you want to change the base?
feat: implement custom message routing system #128
Conversation
Original prompt from Erkin |
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
@samuelscheit review appreciated |
- Add RoutingRule entity with 8 required fields and channel relationships - Add MANAGE_ROUTING permission flag (BitFlag 39) for guild-level checks - Implement routing rule validation (invariants, permission checks) - Implement routing rule deletion validation logic - Integrate routing logic into message processing with: - Deduplication per sink channel using Map - Idempotency using nonce to prevent duplicates across workers - Prevention of re-routing already routed messages - DB-side time-window filtering for performance - Create API endpoints for routing rule management: - POST /routing-rules (create with MANAGE_ROUTING right) - GET /routing-rules (list filtered by permission) - PATCH /routing-rules/:id (update valid_until only) - DELETE /routing-rules/:id (delete with validation) Signed-off-by: Devin AI <devin@cognition.ai> For: Erkin Alp Güney <erkinalp9035@gmail.com> Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
…alidity check The validity check (valid_since and valid_until) should apply to when the message was sent, not when the routing rules are being processed. This fix uses message.id (which is a Snowflake containing the send timestamp) instead of generating a new Snowflake with the current time. This ensures routing rules are evaluated based on when the message was originally sent, which is the correct behavior for time-based routing. Signed-off-by: Devin AI <devin@cognition.ai> For: Erkin Alp Güney <erkinalp9035@gmail.com> Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
- Store messages once in storage_channel instead of copying to sink channels - Add source_channel_id field to Message entity to track original posted channel - Implement projection helpers: computeProjectionsForMessage, isMessageVisibleInChannelForUser, resolveMessageInChannel, getProjectedMessagesForChannel - Update sendMessage to evaluate routing rules and store in storage_channel with source_channel_id set - Emit MESSAGE_CREATE to all projection channels (source + sinks) with proper channel_id override - Update all message endpoints to use resolveMessageInChannel for projection validation - Update GET /channels/:id/messages to use projection query logic - Update message operations (edit, delete, reactions, pins, replies, threads) to emit events to all projections - Add database migrations for source_channel_id field (postgres, mysql, mariadb) - Messages now keep same ID across all projections (fixes reactions/replies/references) - Implement target_users filtering at read/dispatch time as required by architecture Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
- Add VIEW_CHANNEL permission enforcement in isMessageVisibleInChannelForUser() - Sanitize routing rule relations for non-admin users in GET /routing-rules - Fix RoutingRule.canDelete() to check storage_channel instead of sink_channel - Prevent information leakage of channels users cannot view Security improvements: - Users can only see projected messages in channels they have VIEW_CHANNEL permission for - Non-admin users cannot see storage/sink channel details for channels they lack permissions on - Rule deletion now correctly checks for affected messages in the projection architecture Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
7a011e1 to
6c02b09
Compare
|
Devin is currently unreachable - the session may have died. |
|
Looks like there are a few issues preventing this PR from being merged!
If you'd like me to help, just leave a comment, like Feel free to include any additional details that might help me get this PR into a better state. You can manage your notification settings |
- Add target_role_id and target_role_guild_id fields to RoutingRule - When target_role_id is set with null guild, treat as intimacy broadcast - In intimacy broadcast mode, ignore storage_channel (store in source) - Filter reactions so recipients only see their own reactions (sender sees all) - Update reactions endpoint to filter reactions for intimacy broadcast - Update messages endpoint to filter reactions for intimacy broadcast Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
Custom Message Routing System with No-Copy Projection Architecture
This PR implements a complete custom message routing system that allows messages to be projected across multiple channels without duplication, using a no-copy projection architecture.
What This PR Adds
New Features
1. RoutingRule Entity and API (
src/util/entities/RoutingRule.ts,src/api/routes/routing-rules.ts)source_channel_id,storage_channel_id,sink_channel_id,source_users,target_users,valid_since,valid_untilvalid_untilcan be updated while rule is in effect)MANAGE_ROUTINGright)2. No-Copy Projection Architecture (
src/api/util/helpers/MessageProjection.ts)storage_channelinstead of being copied to sink channelscomputeProjectionsForMessage(): determines all channels where a message should appearisMessageVisibleInChannelForUser(): validates projection visibility with target_users filteringresolveMessageInChannel(): fetches and validates messages in a specific channel contextgetProjectedMessagesForChannel(): queries all projected messages for a channel with proper filtering3. Message Entity Updates (
src/util/entities/Message.ts)source_channel_idfield to track where messages were originally postedtoProjectedJSON(channelId)method to override channel_id in API responses4. Updated Message Handling (
src/api/util/handlers/Message.ts)sendMessage()now evaluates routing rules using message ID timestamps (not wall-clock time)storage_channelwithsource_channel_idset to original channel5. Updated Message Endpoints
/channels/:id/messages/:iduseresolveMessageInChannel()/channels/:id/messagesusesgetProjectedMessagesForChannel()Updates Since Last Revision: Simulated Intimacy Broadcast DM
New Feature: Intimacy Broadcast Mode (similar to OnlyFans-style DM broadcasts)
When a routing rule has
target_role_idset withtarget_role_guild_id = null, the system treats this as a "simulated intimacy broadcast DM":New additions:
target_role_idandtarget_role_guild_idfields onRoutingRuleentityisIntimacyBroadcast()method to detect intimacy broadcast modefilterReactionsForIntimacyBroadcast()function in MessageProjection.tsgetIntimacyBroadcastRuleForChannel()helper function/channels/:id/messagesand GET/channels/:id/messages/:id/reactions/:emojiendpoints to filter reactions for intimacy broadcastArchitecture Benefits
No Message Duplication: Messages are stored once in storage_channel, eliminating data duplication and ensuring consistency.
Same ID Across Projections: Messages keep the same ID in all channels where they appear, fixing reactions/replies/references that would break with copied messages.
Target Users Filtering: The
target_usersfield is enforced at read/dispatch time, allowing fine-grained control over who can see routed messages in sink channels.Route Immutability: Routes are immutable (except
valid_until) because we need them to trace back which storage_channel a message belongs to. Without immutability, we'd need to move messages in bulk when routes change.Timestamp-Based Evaluation: Routing rules are evaluated using message timestamps (from Snowflake IDs), not current time, ensuring consistent routing regardless of when queries are executed.
Database Changes
source_channel_idcolumn to messages table (nullable, indexed)routing_rulestable with all routing rule fields (including newtarget_role_idandtarget_role_guild_id)Summary
This PR extends the custom message routing system to support a new "intimacy broadcast" mode where recipients cannot see each other's reactions, simulating private DM-like interactions in a broadcast context.
Review & Testing Checklist for Human
target_role_idset andtarget_role_guild_id = null, add reactions from multiple users, and verify each recipient only sees their own reactions while the author sees allRecommended Test Plan:
target_role_idpointing to a role andtarget_role_guild_id = nullNotes
filterReactionsForIntimacyBroadcast()returns null for reactions where the viewing user hasn't reacted, which are then filtered outLink to Devin runs:
Requested by: Erkin Alp Güney (erkinalp9035@gmail.com) / @erkinalp