Handle action-response ephemerals and MSS errors

Register and handle action-response ephemeral events and improve MSS error reporting across the bridge.

- Add ErrActionResponseNotSupported and register BeeperActionResponse event handler in the Matrix connector.
- Short-circuit AI stream ephemerals when ignored in matrix event handling.
- Return EventHandlingResult* values with MSS error context in portal ephemeral handling (failed and success paths) and return Ignored with ErrActionResponseNotSupported when network API lacks action-response support.
- Minor cleanup: remove/adjust redundant comments, small formatting tweaks, and update copyright years in tests.
This commit is contained in:
batuhan içöz 2026-03-02 06:20:49 +01:00
commit de8b6bbbc8
No known key found for this signature in database
10 changed files with 13 additions and 15 deletions

View file

@ -75,6 +75,7 @@ var (
ErrMediaConvertFailed error = WrapErrorInStatus(errors.New("failed to convert media")).WithMessage("failed to convert media").WithIsCertain(true).WithSendNotice(true)
ErrMembershipNotSupported error = WrapErrorInStatus(errors.New("this bridge does not support changing group membership")).WithIsCertain(true).WithErrorAsMessage().WithSendNotice(false).WithErrorReason(event.MessageStatusUnsupported)
ErrDeleteChatNotSupported error = WrapErrorInStatus(errors.New("this bridge does not support deleting chats")).WithIsCertain(true).WithErrorAsMessage().WithSendNotice(false).WithErrorReason(event.MessageStatusUnsupported)
ErrActionResponseNotSupported error = WrapErrorInStatus(errors.New("this bridge does not support action responses")).WithIsCertain(true).WithErrorAsMessage().WithSendNotice(false).WithErrorReason(event.MessageStatusUnsupported)
ErrPowerLevelsNotSupported error = WrapErrorInStatus(errors.New("this bridge does not support changing group power levels")).WithIsCertain(true).WithErrorAsMessage().WithSendNotice(false).WithErrorReason(event.MessageStatusUnsupported)
ErrRemoteEchoTimeout = WrapErrorInStatus(errors.New("remote echo timed out")).WithIsCertain(false).WithSendNotice(true).WithErrorReason(event.MessageStatusTooOld)
ErrRemoteAckTimeout = WrapErrorInStatus(errors.New("remote ack timed out")).WithIsCertain(false).WithSendNotice(true).WithErrorReason(event.MessageStatusTooOld)

View file

@ -155,6 +155,7 @@ func (br *Connector) Init(bridge *bridgev2.Bridge) {
br.EventProcessor.On(event.StateBeeperDisappearingTimer, br.handleRoomEvent)
br.EventProcessor.On(event.BeeperDeleteChat, br.handleRoomEvent)
br.EventProcessor.On(event.BeeperAcceptMessageRequest, br.handleRoomEvent)
br.EventProcessor.On(event.BeeperActionResponse, br.handleRoomEvent)
br.EventProcessor.On(event.EphemeralEventReceipt, br.handleEphemeralEvent)
br.EventProcessor.On(event.EphemeralEventTyping, br.handleEphemeralEvent)
br.EventProcessor.On(event.EphemeralEventAIStream, br.handleEphemeralEvent)

View file

@ -68,6 +68,10 @@ func (br *Connector) handleEphemeralEvent(ctx context.Context, evt *event.Event)
case event.EphemeralEventTyping:
typingContent := evt.Content.AsTyping()
typingContent.UserIDs = slices.DeleteFunc(typingContent.UserIDs, br.shouldIgnoreEventFromUser)
case event.EphemeralEventAIStream:
if br.shouldIgnoreEvent(evt) {
return
}
}
br.Bridge.QueueMatrixEvent(ctx, evt)
}

View file

@ -218,7 +218,6 @@ type MarkAsDMMatrixAPI interface {
MarkAsDM(ctx context.Context, roomID id.RoomID, otherUser id.UserID) error
}
// EphemeralSendingMatrixAPI extends MatrixAPI with ephemeral event delivery.
type EphemeralSendingMatrixAPI interface {
MatrixAPI
SendEphemeralEvent(ctx context.Context, roomID id.RoomID, eventType event.Type, content *event.Content, txnID string) (*mautrix.RespSendEvent, error)

View file

@ -657,10 +657,8 @@ type TypingHandlingNetworkAPI interface {
HandleMatrixTyping(ctx context.Context, msg *MatrixTyping) error
}
// EphemeralHandlingNetworkAPI is an optional interface that network connectors can implement to handle ephemeral events.
type EphemeralHandlingNetworkAPI interface {
NetworkAPI
// HandleMatrixEphemeral is called when a custom ephemeral event is sent in a portal room.
HandleMatrixEphemeral(ctx context.Context, msg *MatrixEphemeralEvent) error
}

View file

@ -954,7 +954,7 @@ func (portal *Portal) handleMatrixEphemeral(ctx context.Context, sender *User, e
login, _, err := portal.FindPreferredLogin(ctx, sender, true)
if err != nil {
log.Err(err).Msg("Failed to get user login to handle ephemeral event")
return EventHandlingResultFailed
return EventHandlingResultFailed.WithMSSError(err)
}
var origSender *OrigSender
if login == nil {
@ -982,9 +982,9 @@ func (portal *Portal) handleMatrixEphemeral(ctx context.Context, sender *User, e
})
if err != nil {
log.Err(err).Msg("Failed to bridge Matrix ephemeral event")
return EventHandlingResultFailed
return EventHandlingResultFailed.WithMSSError(err)
}
return EventHandlingResultSuccess
return EventHandlingResultSuccess.WithMSS()
}
func (portal *Portal) sendTypings(ctx context.Context, userIDs []id.UserID, typing bool) {
@ -1878,7 +1878,7 @@ func (portal *Portal) handleMatrixActionResponse(
}
api, ok := sender.Client.(ActionResponseHandlingNetworkAPI)
if !ok {
return EventHandlingResultIgnored
return EventHandlingResultIgnored.WithMSSError(ErrActionResponseNotSupported)
}
err := api.HandleMatrixActionResponse(ctx, &MatrixActionResponse{
Event: evt,

View file

@ -1,4 +1,4 @@
// Copyright (c) 2020 Tulir Asokan
// Copyright (c) 2026 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this

View file

@ -214,8 +214,6 @@ func (content *MessageEventContent) RemovePerMessageProfileFallback() {
}
}
// BeeperActionHint represents a single button in a com.beeper.action_hints content block.
// Based on MSC1485 (tulir).
type BeeperActionHint struct {
Body string `json:"body"`
EventType string `json:"event_type,omitempty"`
@ -225,8 +223,6 @@ type BeeperActionHint struct {
Img id.ContentURI `json:"img,omitempty"`
}
// BeeperActionHints is a single object under "com.beeper.action_hints" containing
// both the hints array (from MSC1485) and Beeper extension fields.
type BeeperActionHints struct {
Hints []BeeperActionHint `json:"hints"`
Exclusive bool `json:"exclusive,omitempty"`
@ -235,7 +231,6 @@ type BeeperActionHints struct {
Context json.RawMessage `json:"context,omitempty"`
}
// BeeperActionResponseEventContent represents the content of a com.beeper.action_response event.
type BeeperActionResponseEventContent struct {
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
ActionID string `json:"action_id,omitempty"`

View file

@ -1,4 +1,4 @@
// Copyright (c) 2020 Tulir Asokan
// Copyright (c) 2026 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this

View file

@ -206,7 +206,7 @@ var (
StateElementFunctionalMembers = Type{"io.element.functional_members", StateEventType}
StateBeeperRoomFeatures = Type{"com.beeper.room_features", StateEventType}
StateBeeperDisappearingTimer = Type{"com.beeper.disappearing_timer", StateEventType}
StateMSC4391BotCommand = Type{"org.matrix.msc4391.command_description", StateEventType}
StateMSC4391BotCommand = Type{"org.matrix.msc4391.command_description", StateEventType}
)
// Message events