diff --git a/bridgev2/errors.go b/bridgev2/errors.go index 2834b298..1b168e8e 100644 --- a/bridgev2/errors.go +++ b/bridgev2/errors.go @@ -58,6 +58,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) ErrPowerLevelsNotSupported error = WrapErrorInStatus(errors.New("this bridge does not support changing group power levels")).WithIsCertain(true).WithErrorAsMessage().WithSendNotice(false) + ErrJoinRuleNotSupported error = WrapErrorInStatus(errors.New("this bridge does not support changing group join rule")).WithIsCertain(true).WithErrorAsMessage().WithSendNotice(false) ) // RespError is a class of error that certain network interface methods can return to ensure that the error diff --git a/bridgev2/matrix/connector.go b/bridgev2/matrix/connector.go index dee28b8d..80dd06cf 100644 --- a/bridgev2/matrix/connector.go +++ b/bridgev2/matrix/connector.go @@ -142,6 +142,7 @@ func (br *Connector) Init(bridge *bridgev2.Bridge) { br.EventProcessor.On(event.StateTopic, br.handleRoomEvent) br.EventProcessor.On(event.EphemeralEventReceipt, br.handleEphemeralEvent) br.EventProcessor.On(event.EphemeralEventTyping, br.handleEphemeralEvent) + br.EventProcessor.On(event.StateJoinRules, br.handleRoomEvent) br.Bot = br.AS.BotIntent() br.Crypto = NewCryptoHelper(br) br.Bridge.Commands.(*commands.Processor).AddHandlers( diff --git a/bridgev2/networkinterface.go b/bridgev2/networkinterface.go index b68ad0c9..be65de27 100644 --- a/bridgev2/networkinterface.go +++ b/bridgev2/networkinterface.go @@ -688,6 +688,13 @@ type PowerLevelHandlingNetworkAPI interface { HandleMatrixPowerLevels(ctx context.Context, msg *MatrixPowerLevelChange) (bool, error) } +type MatrixJoinRule MatrixRoomMeta[*event.JoinRulesEventContent] + +type JoinRulesHandlingNetworkAPI interface { + NetworkAPI + HandleMatrixJoinRules(ctx context.Context, msg *MatrixJoinRule) (bool, error) +} + type PushType int func (pt PushType) String() string { diff --git a/bridgev2/portal.go b/bridgev2/portal.go index fc047beb..887598d8 100644 --- a/bridgev2/portal.go +++ b/bridgev2/portal.go @@ -496,6 +496,8 @@ func (portal *Portal) handleMatrixEvent(sender *User, evt *event.Event) { portal.handleMatrixMembership(ctx, login, origSender, evt) case event.StatePowerLevels: portal.handleMatrixPowerLevels(ctx, login, origSender, evt) + case event.StateJoinRules: + portal.handleMatrixJoinRules(ctx, login, origSender, evt) } } @@ -1333,6 +1335,46 @@ func (portal *Portal) handleMatrixPowerLevels( } } +func (portal *Portal) handleMatrixJoinRules( + ctx context.Context, + sender *UserLogin, + origSender *OrigSender, + evt *event.Event, +) { + log := zerolog.Ctx(ctx) + content, ok := evt.Content.Parsed.(*event.JoinRulesEventContent) + if !ok { + log.Error().Type("content_type", evt.Content.Parsed).Msg("Unexpected parsed content type") + portal.sendErrorStatus(ctx, evt, fmt.Errorf("%w: %T", ErrUnexpectedParsedContentType, evt.Content.Parsed)) + return + } + api, ok := sender.Client.(JoinRulesHandlingNetworkAPI) + if !ok { + portal.sendErrorStatus(ctx, evt, ErrJoinRuleNotSupported) + return + } + prevContent := &event.JoinRulesEventContent{} + if evt.Unsigned.PrevContent != nil { + _ = evt.Unsigned.PrevContent.ParseRaw(evt.Type) + prevContent, _ = evt.Unsigned.PrevContent.Parsed.(*event.JoinRulesEventContent) + } + joinRuleChange := &MatrixJoinRule{ + MatrixEventBase: MatrixEventBase[*event.JoinRulesEventContent]{ + Event: evt, + Content: content, + Portal: portal, + OrigSender: origSender, + }, + PrevContent: prevContent, + } + _, err := api.HandleMatrixJoinRules(ctx, joinRuleChange) + if err != nil { + log.Err(err).Msg("Failed to handle Matrix join rule change") + portal.sendErrorStatus(ctx, evt, err) + return + } +} + func (portal *Portal) handleMatrixRedaction(ctx context.Context, sender *UserLogin, origSender *OrigSender, evt *event.Event) { log := zerolog.Ctx(ctx) content, ok := evt.Content.Parsed.(*event.RedactionEventContent)