mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-06 23:39:54 +00:00
628b00eb08
* core: channel messages (WIP) * do not include member ID when quoting channel messages * query plans * reduce duplication * refactor * refactor plan * refactor 2 * all tests * remove plan * refactor 3 * refactor 4 * refactor 5 * refactor 6 * plans * plans to imrove test coverage and fix bugs * update plan * update plan * bug fixes (wip) * new plan * fixes wip * more tests * comment, fix lint * restore comment * restore comments * rename param * move type * simplify * comment * fix stale state * refactor * less diff * simplify * less diff * refactor --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
322 lines
11 KiB
Markdown
322 lines
11 KiB
Markdown
# Plan: Channel Message Bugs Fix
|
|
|
|
## Table of Contents
|
|
1. [Executive Summary](#executive-summary)
|
|
2. [Bug 1: Delivery Context Flag](#bug-1-delivery-context-flag)
|
|
3. [Bug 2: Reaction Attribution](#bug-2-reaction-attribution)
|
|
4. [Bug 3: Update Fallback Default](#bug-3-update-fallback-default)
|
|
5. [Bug 4: Forward API Parameter](#bug-4-forward-api-parameter)
|
|
6. [Bug 5: CLI Forward Hardcode](#bug-5-cli-forward-hardcode)
|
|
7. [Test Plan](#test-plan)
|
|
8. [Implementation Order](#implementation-order)
|
|
|
|
---
|
|
|
|
## Executive Summary
|
|
|
|
**5 bugs identified** in channel message handling:
|
|
|
|
| # | Location | Bug | Severity |
|
|
|---|----------|-----|----------|
|
|
| 1 | Subscriber.hs:935-945 | Events use `isChannelOwner` instead of item's `showGroupAsSender` | Critical |
|
|
| 2 | Subscriber.hs:1818-1842 | Reactions allow `m_=Nothing` and fall back to membership | High |
|
|
| 3 | Subscriber.hs:1950-1969 | Update fallback creates item without correct sendAsGroup flag | Medium |
|
|
| 4 | Commands.hs:930,944 | Forward API ignores `_sendAsGroup` parameter | High |
|
|
| 5 | Commands.hs:2191,2196,2201,4633 | CLI forward hardcodes False | Medium |
|
|
|
|
---
|
|
|
|
## Bug 1: Delivery Context Flag
|
|
|
|
### Current Code (Subscriber.hs:935-945)
|
|
```haskell
|
|
let isChannelOwner = useRelays' gInfo' && memberRole' m'' == GROwner
|
|
showGroupAsSender' = case event of
|
|
XMsgNew mc -> fromMaybe False (asGroup (mcExtMsgContent mc))
|
|
XMsgUpdate {} -> isChannelOwner -- BUG: should use item's flag
|
|
XMsgDel {} -> isChannelOwner -- BUG
|
|
XMsgReact {} -> isChannelOwner -- BUG
|
|
XMsgFileDescr {} -> isChannelOwner -- BUG
|
|
XFileCancel {} -> isChannelOwner -- BUG
|
|
_ -> False
|
|
```
|
|
|
|
### Problem
|
|
Events referencing existing items (update, delete, react, file) compute `showGroupAsSender'` from **current sender role** (`isChannelOwner`) instead of **item's stored `showGroupAsSender` flag**.
|
|
|
|
### Fix
|
|
Extract `showGroupAsSender` from the chat item being referenced:
|
|
|
|
```haskell
|
|
showGroupAsSender' = case event of
|
|
XMsgNew mc -> fromMaybe False (asGroup (mcExtMsgContent mc))
|
|
XMsgUpdate {} -> itemShowGroupAsSender ci -- from item lookup
|
|
XMsgDel {} -> itemShowGroupAsSender ci
|
|
XMsgReact {} -> itemShowGroupAsSender ci
|
|
XMsgFileDescr {} -> itemShowGroupAsSender ci
|
|
XFileCancel {} -> itemShowGroupAsSender ci
|
|
_ -> False
|
|
```
|
|
|
|
**Note:** Use `chatDir` from ChatItem and pattern match on `CIChannelRcv` to determine sendAsGroup flag.
|
|
|
|
### Files Modified
|
|
- `src/Simplex/Chat/Library/Subscriber.hs`: Lines 935-945
|
|
|
|
---
|
|
|
|
## Bug 2: Reaction Attribution
|
|
|
|
### Current Code (Subscriber.hs:1818-1842)
|
|
```haskell
|
|
groupMsgReaction :: GroupInfo -> Maybe GroupMember -> SharedMsgId -> Maybe MemberId -> Maybe MsgScope -> MsgReaction -> Bool -> RcvMessage -> UTCTime -> CM (Maybe DeliveryJobScope)
|
|
groupMsgReaction g m_ sharedMsgId itemMemberId scope_ reaction add RcvMessage {msgId} brokerTs
|
|
...
|
|
where
|
|
GroupInfo {membership} = g
|
|
reactor = fromMaybe membership m_ -- BUG (line 1842): uses membership when m_ is Nothing
|
|
ciDir = maybe CIChannelRcv CIGroupRcv m_
|
|
```
|
|
|
|
### Problem
|
|
When `m_` is `Nothing`, reactor incorrectly falls back to `membership` (user's own member record). However, reactions should always come from an identifiable member - the `m_` parameter should never be `Nothing` for reactions.
|
|
|
|
### Fix
|
|
Reactions can only come from members (including owners), never from channels. XMsgReact handler must be reworked to require `GroupMember` instead of `Maybe GroupMember`. The `m_` parameter should not be optional for reactions.
|
|
|
|
### Files Modified
|
|
- `src/Simplex/Chat/Library/Subscriber.hs`: Lines 1818-1842
|
|
|
|
---
|
|
|
|
## Bug 3: Update Fallback Default
|
|
|
|
### Current Code (Subscriber.hs:1950-1969)
|
|
```haskell
|
|
updateRcvChatItem `catchCINotFound` \_ -> do
|
|
(chatDir, mentions', scopeInfo) <- case m_ of
|
|
Just m -> ...
|
|
Nothing -> pure (CDChannelRcv gInfo Nothing, M.empty, Nothing) -- BUG: no sendAsGroup info
|
|
(ci, cInfo) <- saveRcvChatItem' user chatDir msg ...
|
|
```
|
|
|
|
### Problem
|
|
When `x.msg.update` arrives for a locally-deleted item in a channel (`m_` is `Nothing`), the fallback creates a new item with `CDChannelRcv gInfo Nothing` but doesn't know the original item's `sendAsGroup` flag.
|
|
|
|
### Fix (Option B: Require sender to include flag in the event)
|
|
Add `asGroup` field to `XMsgUpdate` message format.
|
|
|
|
**Rationale:** We don't know what owner wants otherwise - it may send as channel or it may send as owner, and different members must have the same view (e.g. when multiple relays are used, it would be random).
|
|
|
|
### Files Modified
|
|
- `src/Simplex/Chat/Library/Subscriber.hs`: Lines 1950-1969
|
|
- Protocol message format (XMsgUpdate)
|
|
|
|
---
|
|
|
|
## Bug 4: Forward API Parameter
|
|
|
|
### Current Code (Commands.hs:930,944)
|
|
```haskell
|
|
APIForwardChatItems ... _sendAsGroup -> withUser $ \user -> case toCType of
|
|
CTGroup -> do
|
|
...
|
|
sendGroupContentMessages user gInfo toScope (sendAsGroup' gInfo) False itemTTL cmrs'
|
|
-- ^^^^^^^^^^^^^^^^^^^ BUG: ignores _sendAsGroup
|
|
```
|
|
|
|
### Problem
|
|
The `_sendAsGroup` parameter is received but ignored. The function computes its own `sendAsGroup' gInfo` instead.
|
|
|
|
### Fix
|
|
```haskell
|
|
APIForwardChatItems ... sendAsGroup -> withUser $ \user -> case toCType of
|
|
CTGroup -> do
|
|
...
|
|
sendGroupContentMessages user gInfo toScope sendAsGroup False itemTTL cmrs'
|
|
```
|
|
|
|
### Files Modified
|
|
- `src/Simplex/Chat/Library/Commands.hs`: Line 930 (rename parameter), Line 944 (use parameter)
|
|
|
|
---
|
|
|
|
## Bug 5: CLI Forward Hardcode
|
|
|
|
### Current Code (Commands.hs)
|
|
```haskell
|
|
-- Line 2191
|
|
processChatCommand vr nm $ APIForwardChatItems toChatRef (ChatRef CTDirect contactId Nothing) (forwardedItemId :| []) Nothing False
|
|
|
|
-- Line 2196
|
|
processChatCommand vr nm $ APIForwardChatItems toChatRef (ChatRef CTGroup groupId Nothing) (forwardedItemId :| []) Nothing False
|
|
|
|
-- Line 2201
|
|
processChatCommand vr nm $ APIForwardChatItems toChatRef (ChatRef CTLocal folderId Nothing) (forwardedItemId :| []) Nothing False
|
|
|
|
-- Line 4633
|
|
"/_forward " *> (APIForwardChatItems <$> chatRefP <* A.space <*> chatRefP <*> _strP <*> sendMessageTTLP <*> pure False),
|
|
```
|
|
|
|
### Problem
|
|
All CLI forward commands hardcode `False` for `sendAsGroup` instead of computing based on destination.
|
|
|
|
### Fix
|
|
Compute `sendAsGroup` before calling API based on destination group's channel status:
|
|
|
|
```haskell
|
|
-- Lines 2191, 2196, 2201: Need to determine sendAsGroup based on toChatRef
|
|
-- If toChatRef is a channel and user is owner, sendAsGroup should default to True
|
|
|
|
-- Line 4633: Parser should accept optional flag (parser cannot know context)
|
|
```
|
|
|
|
### Files Modified
|
|
- `src/Simplex/Chat/Library/Commands.hs`: Lines 2191, 2196, 2201, 4633
|
|
|
|
---
|
|
|
|
## Test Plan
|
|
|
|
### New Tests (8 total)
|
|
|
|
Tests 1-4 cover Bug 1 (delivery context flag). Each tests a specific event type where the owner sends as member (sendAsGroup=False). Existing tests already cover the "sends as channel" (sendAsGroup=True) case; these tests verify that the delivery context correctly uses the item's stored sendAsGroup=False flag rather than recomputing from the owner's current role.
|
|
|
|
#### Test 1: `testChannelOwnerUpdateAsMember`
|
|
**Objective:** Verify x.msg.update uses item's sendAsGroup=False, not current role.
|
|
|
|
**Scenario:**
|
|
1. Owner sends message as member (sendAsGroup=False)
|
|
2. Member receives message, verify it shows as from member (not channel)
|
|
3. Owner updates message
|
|
4. Verify update delivery context uses sendAsGroup=False from the item, not recomputed from owner role
|
|
|
|
**Coverage:** Bug 1
|
|
|
|
---
|
|
|
|
#### Test 2: `testChannelOwnerDeleteAsMember`
|
|
**Objective:** Verify x.msg.del uses item's sendAsGroup=False, not current role.
|
|
|
|
**Scenario:**
|
|
1. Owner sends message as member (sendAsGroup=False)
|
|
2. Member receives message, verify it shows as from member (not channel)
|
|
3. Owner deletes message
|
|
4. Verify delete delivery context uses sendAsGroup=False from the item, not recomputed from owner role
|
|
|
|
**Coverage:** Bug 1
|
|
|
|
---
|
|
|
|
#### Test 3: `testChannelOwnerFileTransferAsMember`
|
|
**Objective:** Verify file delivery (including x.msg.file.descr) uses item's sendAsGroup=False, not current role.
|
|
|
|
**Scenario:**
|
|
1. Owner sends file as member (sendAsGroup=False)
|
|
2. Member receives file, verify it shows as from member (not channel)
|
|
3. Verify file delivery uses sendAsGroup=False from the item, not recomputed from owner role
|
|
|
|
**Note:** x.msg.file.descr is part of file delivery, not a separate event to test independently.
|
|
|
|
**Coverage:** Bug 1
|
|
|
|
---
|
|
|
|
#### Test 4: `testChannelOwnerFileCancelAsMember`
|
|
**Objective:** Verify x.file.cancel uses item's sendAsGroup=False, not current role.
|
|
|
|
**Scenario:**
|
|
1. Owner sends file as member (sendAsGroup=False)
|
|
2. Member receives file, verify it shows as from member (not channel)
|
|
3. Owner cancels file
|
|
4. Verify cancel delivery context uses sendAsGroup=False from the item, not recomputed from owner role
|
|
|
|
**Coverage:** Bug 1
|
|
|
|
---
|
|
|
|
#### Test 5: `testChannelReactionAttribution`
|
|
**Objective:** Verify reactions require a member sender (not optional).
|
|
|
|
**Scenario:**
|
|
1. Owner sends channel message
|
|
2. Owner adds reaction (as member, not as channel)
|
|
3. Verify reaction is attributed to owner's member record
|
|
4. Member adds reaction to channel message
|
|
5. Verify member reaction is attributed correctly
|
|
6. Verify channel cannot send reactions (m_ must be Just)
|
|
|
|
**Coverage:** Bug 2
|
|
|
|
---
|
|
|
|
#### Test 6: `testChannelUpdateFallbackSendAsGroup`
|
|
**Objective:** Verify update on deleted item creates correct sendAsGroup from protocol field.
|
|
|
|
**Scenario:**
|
|
1. Owner sends channel message (sendAsGroup=True)
|
|
2. Member receives and locally deletes
|
|
3. Owner updates message (XMsgUpdate includes asGroup=True)
|
|
4. Verify member's recreated item has sendAsGroup=True
|
|
5. Owner sends message as member (sendAsGroup=False)
|
|
6. Member receives and locally deletes
|
|
7. Owner updates message (XMsgUpdate includes asGroup=False)
|
|
8. Verify member's recreated item has sendAsGroup=False
|
|
|
|
**Coverage:** Bug 3
|
|
|
|
---
|
|
|
|
#### Test 7: `testForwardAPIUsesParameter`
|
|
**Objective:** Verify Forward API respects sendAsGroup parameter.
|
|
|
|
**Scenario:**
|
|
1. Create channel with owner
|
|
2. Forward message to channel with sendAsGroup=True
|
|
3. Verify message sent as channel
|
|
4. Forward message with sendAsGroup=False
|
|
5. Verify message sent as member
|
|
|
|
**Coverage:** Bug 4
|
|
|
|
---
|
|
|
|
#### Test 8: `testForwardCLISendAsGroup`
|
|
**Objective:** Verify CLI forward commands compute sendAsGroup correctly.
|
|
|
|
**Scenario:**
|
|
1. Create channel with owner
|
|
2. Use `/forward` to forward to channel
|
|
3. Verify sendAsGroup computed correctly (True for owner in channel)
|
|
|
|
**Coverage:** Bug 5
|
|
|
|
---
|
|
|
|
## Implementation Order
|
|
|
|
### Phase 1: Critical Fix (Bug 1)
|
|
1. Fix delivery context in Subscriber.hs
|
|
2. Add Tests 1-4 (`testChannelOwnerUpdateAsMember`, `testChannelOwnerDeleteAsMember`, `testChannelOwnerFileTransferAsMember`, `testChannelOwnerFileCancelAsMember`)
|
|
|
|
### Phase 2: API Fixes (Bugs 4, 5)
|
|
1. Fix Forward API parameter usage
|
|
2. Fix CLI forward hardcodes
|
|
3. Add Tests 7 and 8 (`testForwardAPIUsesParameter`, `testForwardCLISendAsGroup`)
|
|
|
|
### Phase 3: Behavior Fixes (Bugs 2, 3)
|
|
1. Rework XMsgReact handler to require GroupMember (not Maybe GroupMember)
|
|
2. Add asGroup field to XMsgUpdate protocol message
|
|
3. Add Tests 5 and 6 (`testChannelReactionAttribution`, `testChannelUpdateFallbackSendAsGroup`)
|
|
|
|
---
|
|
|
|
## Files Summary
|
|
|
|
| File | Changes |
|
|
|------|---------|
|
|
| `src/Simplex/Chat/Library/Subscriber.hs` | Lines 935-945 (Bug 1), 1818-1842 (Bug 2), 1950-1969 (Bug 3) |
|
|
| `src/Simplex/Chat/Library/Commands.hs` | Lines 930,944 (Bug 4), 2191,2196,2201,4633 (Bug 5) |
|
|
| Protocol message types | Add asGroup field to XMsgUpdate (Bug 3) |
|
|
| `tests/ChatTests/Groups.hs` | Add 8 new tests |
|