From 55dda534d202f482f50559538814785abf5619b5 Mon Sep 17 00:00:00 2001 From: Nick Mills-Barrett Date: Fri, 9 Oct 2026 12:47:00 +0100 Subject: [PATCH] handlematrix: reject non-admin posts in community announcement groups Non-admins can only comment in community announcement groups, but top-level messages and polls were sent anyway, silently dropped by WhatsApp and reported as successful. Fail those sends with a no-permission status when the user isn't a community admin. --- pkg/connector/handlematrix.go | 24 ++++++++++++++++++++++++ pkg/msgconv/from-matrix.go | 1 - 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index ee3d70a..426445d 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -67,6 +67,10 @@ var ( ) func (wa *WhatsAppClient) HandleMatrixPollStart(ctx context.Context, msg *bridgev2.MatrixPollStart) (result *bridgev2.MatrixMessageResponse, retErr error) { + err := wa.checkCanSendAnnouncement(ctx, msg.Portal) + if err != nil { + return nil, err + } waMsg, optionMap, err := wa.Main.MsgConv.PollStartToWhatsApp(ctx, msg.Content, msg.ReplyTo, msg.Portal) if err != nil { return nil, fmt.Errorf("failed to convert poll vote: %w", err) @@ -96,6 +100,12 @@ func (wa *WhatsAppClient) HandleMatrixPollVote(ctx context.Context, msg *bridgev } func (wa *WhatsAppClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2.MatrixMessage) (result *bridgev2.MatrixMessageResponse, retErr error) { + if msg.ThreadRoot == nil { + err := wa.checkCanSendAnnouncement(ctx, msg.Portal) + if err != nil { + return nil, err + } + } waMsg, req, err := wa.Main.MsgConv.ToWhatsApp(ctx, wa.Client, msg.Event, msg.Content, msg.ReplyTo, msg.ThreadRoot, msg.Portal) if err != nil { return nil, fmt.Errorf("failed to convert message: %w", err) @@ -104,8 +114,22 @@ func (wa *WhatsAppClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2 return wa.handleConvertedMatrixMessage(ctx, msg, waMsg, req) } +func (wa *WhatsAppClient) checkCanSendAnnouncement(ctx context.Context, portal *bridgev2.Portal) error { + if !portal.Metadata.(*waid.PortalMetadata).CommunityAnnouncementGroup { + return nil + } + pls, err := wa.Main.Bridge.Matrix.GetPowerLevels(ctx, portal.MXID) + if err != nil { + return fmt.Errorf("failed to get power levels: %w", err) + } else if pls.GetUserLevel(wa.UserLogin.UserMXID) < adminPL { + return ErrNotCommunityAdmin + } + return nil +} + var ErrBroadcastSendDisabled = bridgev2.WrapErrorInStatus(errors.New("sending status messages is disabled")).WithErrorAsMessage().WithIsCertain(true).WithSendNotice(true).WithErrorReason(event.MessageStatusUnsupported) var ErrBroadcastReactionUnsupported = bridgev2.WrapErrorInStatus(errors.New("reacting to status messages is not currently supported")).WithErrorAsMessage().WithIsCertain(true).WithSendNotice(true).WithErrorReason(event.MessageStatusUnsupported) +var ErrNotCommunityAdmin = bridgev2.WrapErrorInStatus(errors.New("only community admins can send announcements")).WithErrorAsMessage().WithIsCertain(true).WithSendNotice(true).WithErrorReason(event.MessageStatusNoPermission) func (wa *WhatsAppClient) handleConvertedMatrixMessage(ctx context.Context, msg *bridgev2.MatrixMessage, waMsg *waE2E.Message, req *whatsmeow.SendRequestExtra) (*bridgev2.MatrixMessageResponse, error) { if req == nil { diff --git a/pkg/msgconv/from-matrix.go b/pkg/msgconv/from-matrix.go index 833813f..192b8a6 100644 --- a/pkg/msgconv/from-matrix.go +++ b/pkg/msgconv/from-matrix.go @@ -193,7 +193,6 @@ func (mc *MessageConverter) ToWhatsApp( DeprecatedLIDSession: ptr.Ptr(false), } } else { - // TODO check permissions? message.MessageContextInfo = &waE2E.MessageContextInfo{ MessageSecret: random.Bytes(32), }