Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handle Membership #527

Merged
merged 5 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
maltee1 marked this conversation as resolved.
Show resolved Hide resolved
_, 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
maltee1 marked this conversation as resolved.
Show resolved Hide resolved
}
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
Loading