Skip to content

Commit

Permalink
handlematrix: add support for most membership changes (#527)
Browse files Browse the repository at this point in the history
Missing knocks and joins

[skip cd]
  • Loading branch information
maltee1 committed Aug 9, 2024
1 parent 77cf7e7 commit c3fd55c
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 11 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ require (
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
golang.org/x/net v0.27.0
google.golang.org/protobuf v1.34.2
maunium.net/go/mautrix v0.19.1-0.20240808174455-5edfcff2b7b6
maunium.net/go/mautrix v0.19.1-0.20240809135320-eb84187368b7
nhooyr.io/websocket v1.8.11
)

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
maunium.net/go/mautrix v0.19.1-0.20240808174455-5edfcff2b7b6 h1:OGskR/suV/6OGgURhucS8eH5QRyRzw7Wkpf8exrOakk=
maunium.net/go/mautrix v0.19.1-0.20240808174455-5edfcff2b7b6/go.mod h1:ZWyxoQxRTBxzWIMs0kQCVogZIY0clTu33h102veCT/Q=
maunium.net/go/mautrix v0.19.1-0.20240809135320-eb84187368b7 h1:xAq4d1rW/5WN7szBtxHNY/63EPNdji+h7FXlLGBUfJU=
maunium.net/go/mautrix v0.19.1-0.20240809135320-eb84187368b7/go.mod h1:ZWyxoQxRTBxzWIMs0kQCVogZIY0clTu33h102veCT/Q=
nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0=
nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
4 changes: 3 additions & 1 deletion pkg/connector/groupinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,9 @@ func (s *SignalClient) catchUpGroup(ctx context.Context, portal *bridgev2.Portal
for _, gc := range groupChanges {
log.Debug().Uint32("current_rev", gc.GroupChange.Revision).Msg("Processing group change")
chatInfoChange := s.groupChangeToChatInfoChange(ctx, gc.GroupChange.Revision, gc.GroupChange)
portal.ProcessChatInfoChange(ctx, s.makeEventSender(gc.GroupChange.SourceACI), s.UserLogin, chatInfoChange, time.UnixMilli(int64(ts)))
if gc.GroupChange.SourceServiceID.Type == libsignalgo.ServiceIDTypeACI {
portal.ProcessChatInfoChange(ctx, s.makeEventSender(gc.GroupChange.SourceServiceID.UUID), s.UserLogin, chatInfoChange, time.UnixMilli(int64(ts)))
}
if gc.GroupChange.Revision == toRevision {
break
}
Expand Down
128 changes: 128 additions & 0 deletions pkg/connector/handlematrix.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"maunium.net/go/mautrix/bridgev2"
"maunium.net/go/mautrix/bridgev2/database"
"maunium.net/go/mautrix/bridgev2/networkid"
"maunium.net/go/mautrix/event"

"go.mau.fi/mautrix-signal/pkg/libsignalgo"
"go.mau.fi/mautrix-signal/pkg/signalid"
Expand Down Expand Up @@ -344,3 +345,130 @@ func (s *SignalClient) HandleMatrixRoomTopic(ctx context.Context, msg *bridgev2.
ModifyDescription: &msg.Content.Topic,
}, nil)
}

func (s *SignalClient) HandleMatrixMembership(ctx context.Context, msg *bridgev2.MatrixMembershipChange) (bool, error) {
var targetIntent bridgev2.MatrixAPI
var targetSignalID uuid.UUID
var err error
if msg.Portal.RoomType == database.RoomTypeDM {
//TODO: this probably needs to revert some changes and clean up the portal on leaves
switch msg.Type {
case bridgev2.Invite:
return false, fmt.Errorf("cannot invite additional user to dm")
default:
return false, nil
}
}
if msg.TargetGhost != nil {
targetIntent = msg.TargetGhost.Intent
targetSignalID, err = signalid.ParseUserID(msg.TargetGhost.ID)
if err != nil {
return false, fmt.Errorf("failed to parse target ghost signal id: %w", err)
}
} else if msg.TargetUserLogin != nil {
targetSignalID, err = signalid.ParseUserLoginID(msg.TargetUserLogin.ID)
if err != nil {
return false, fmt.Errorf("failed to parse target user signal id: %w", err)
}
targetIntent = msg.TargetUserLogin.User.DoublePuppet(ctx)
if targetIntent == nil {
ghost, err := s.Main.Bridge.GetGhostByID(ctx, networkid.UserID(msg.TargetUserLogin.ID))
if err != nil {
return false, fmt.Errorf("failed to get ghost for user: %w", err)
}
targetIntent = ghost.Intent
}
}
log := zerolog.Ctx(ctx).With().
Str("From Membership", string(msg.Type.From)).
Str("To Membership", string(msg.Type.To)).
Logger()
gc := &signalmeow.GroupChange{}
role := signalmeow.GroupMember_DEFAULT
if msg.Type.To == event.MembershipInvite || msg.Type == bridgev2.AcceptKnock {
levels, err := msg.Portal.Bridge.Matrix.GetPowerLevels(ctx, msg.Portal.MXID)
if err != nil {
log.Err(err).Msg("Couldn't get power levels")
if levels.GetUserLevel(targetIntent.GetMXID()) >= 50 {
role = signalmeow.GroupMember_ADMINISTRATOR
}
}
}
switch msg.Type {
case bridgev2.AcceptInvite:
gc.PromotePendingMembers = []*signalmeow.PromotePendingMember{{
ACI: targetSignalID,
}}
case bridgev2.RevokeInvite, bridgev2.RejectInvite:
deletePendingMember := libsignalgo.NewACIServiceID(targetSignalID)
gc.DeletePendingMembers = []*libsignalgo.ServiceID{&deletePendingMember}
case bridgev2.Leave, bridgev2.Kick:
gc.DeleteMembers = []*uuid.UUID{&targetSignalID}
case bridgev2.Invite:
gc.AddMembers = []*signalmeow.AddMember{{
GroupMember: signalmeow.GroupMember{
ACI: targetSignalID,
Role: role,
},
}}
// TODO: joining and knocking requires a way to obtain the invite link
// because the joining/knocking member doesn't have the GroupMasterKey yet
// case bridgev2.Join:
// gc.AddMembers = []*signalmeow.AddMember{{
// GroupMember: signalmeow.GroupMember{
// ACI: targetSignalID,
// Role: role,
// },
// JoinFromInviteLink: true,
// }}
// case bridgev2.Knock:
// gc.AddRequestingMembers = []*signalmeow.RequestingMember{{
// ACI: targetSignalID,
// Timestamp: uint64(time.Now().UnixMilli()),
// }}
case bridgev2.AcceptKnock:
gc.PromoteRequestingMembers = []*signalmeow.RoleMember{{
ACI: targetSignalID,
Role: role,
}}
case bridgev2.RetractKnock, bridgev2.RejectKnock:
gc.DeleteRequestingMembers = []*uuid.UUID{&targetSignalID}
case bridgev2.BanKnocked, bridgev2.BanInvited, bridgev2.BanJoined, bridgev2.BanLeft:
gc.AddBannedMembers = []*signalmeow.BannedMember{{
ServiceID: libsignalgo.NewACIServiceID(targetSignalID),
Timestamp: uint64(time.Now().UnixMilli()),
}}
switch msg.Type {
case bridgev2.BanJoined:
gc.DeleteMembers = []*uuid.UUID{&targetSignalID}
case bridgev2.BanInvited:
deletePendingMember := libsignalgo.NewACIServiceID(targetSignalID)
gc.DeletePendingMembers = []*libsignalgo.ServiceID{&deletePendingMember}
case bridgev2.BanKnocked:
gc.DeleteRequestingMembers = []*uuid.UUID{&targetSignalID}
}
case bridgev2.Unban:
unbanUser := libsignalgo.NewACIServiceID(targetSignalID)
gc.DeleteBannedMembers = []*libsignalgo.ServiceID{&unbanUser}
default:
log.Debug().Msg("unsupported membership change")
return false, nil
}
_, groupID, err := signalid.ParsePortalID(msg.Portal.ID)
if err != nil || groupID == "" {
return false, err
}
gc.Revision = msg.Portal.Metadata.(*signalid.PortalMetadata).Revision + 1
revision, err := s.Client.UpdateGroup(ctx, gc, groupID)
if err != nil {
return false, err
}
if msg.Type == bridgev2.Invite {
err = targetIntent.EnsureJoined(ctx, msg.Portal.MXID)
if err != nil {
return false, err
}
}
msg.Portal.Metadata.(*signalid.PortalMetadata).Revision = revision
return true, nil
}
8 changes: 8 additions & 0 deletions pkg/signalid/ids.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ func ParseUserID(userID networkid.UserID) (uuid.UUID, error) {
}
}

func ParseUserLoginID(userLoginID networkid.UserLoginID) (uuid.UUID, error) {
userID, err := uuid.Parse(string(userLoginID))
if err != nil {
return uuid.Nil, err
}
return userID, nil
}

func ParseUserIDAsServiceID(userID networkid.UserID) (libsignalgo.ServiceID, error) {
return libsignalgo.ServiceIDFromString(string(userID))
}
Expand Down
14 changes: 7 additions & 7 deletions pkg/signalmeow/groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ type RequestingMember struct {
Timestamp uint64
}

type PromotePendingMembers struct {
type PromotePendingMember struct {
ACI uuid.UUID
ProfileKey libsignalgo.ProfileKey
}
Expand All @@ -178,15 +178,15 @@ type BannedMember struct {
type GroupChange struct {
groupMasterKey types.SerializedGroupMasterKey

SourceACI uuid.UUID
SourceServiceID libsignalgo.ServiceID
Revision uint32
AddMembers []*AddMember
DeleteMembers []*uuid.UUID
ModifyMemberRoles []*RoleMember
ModifyMemberProfileKeys []*ProfileKeyMember
AddPendingMembers []*PendingMember
DeletePendingMembers []*libsignalgo.ServiceID
PromotePendingMembers []*GroupMember
PromotePendingMembers []*PromotePendingMember
ModifyTitle *string
ModifyAvatar *string
ModifyDisappearingMessagesDuration *uint32
Expand Down Expand Up @@ -862,9 +862,9 @@ func (cli *Client) decryptGroupChange(ctx context.Context, encryptedGroupChange
return nil, fmt.Errorf("wrong serviceid kind: expected aci, got pni")
}
decryptedGroupChange := &GroupChange{
groupMasterKey: groupMasterKey,
Revision: encryptedActions.Revision,
SourceACI: sourceServiceID.UUID,
groupMasterKey: groupMasterKey,
Revision: encryptedActions.Revision,
SourceServiceID: sourceServiceID,
}

if encryptedActions.ModifyTitle != nil {
Expand Down Expand Up @@ -992,7 +992,7 @@ func (cli *Client) decryptGroupChange(ctx context.Context, encryptedGroupChange
if err != nil {
return nil, err
}
decryptedGroupChange.PromotePendingMembers = append(decryptedGroupChange.PromotePendingMembers, &GroupMember{
decryptedGroupChange.PromotePendingMembers = append(decryptedGroupChange.PromotePendingMembers, &PromotePendingMember{
ACI: *aci,
ProfileKey: *profileKey,
})
Expand Down

0 comments on commit c3fd55c

Please sign in to comment.