Skip to content

Commit

Permalink
Add initial user and room metadata support
Browse files Browse the repository at this point in the history
  • Loading branch information
tulir committed May 28, 2024
1 parent 874712c commit e5e3dd1
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 32 deletions.
2 changes: 1 addition & 1 deletion commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,7 @@ func fnCreate(ce *WrappedCommandEvent) {
avatarSet := false
if ok {
roomAvatarEvent.Content.ParseRaw(event.StateRoomAvatar)
avatarURL = roomAvatarEvent.Content.AsRoomAvatar().URL
avatarURL = roomAvatarEvent.Content.AsRoomAvatar().URL.ParseOrIgnore()
if !avatarURL.IsEmpty() {
avatarBytes, err = ce.Bot.DownloadBytes(ce.Ctx, avatarURL)
if err != nil {
Expand Down
155 changes: 135 additions & 20 deletions connector/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,115 @@ type SignalClient struct {
Client *signalmeow.Client
}

func (s *SignalClient) contactToUserInfo(contact *types.Recipient) *bridgev2.UserInfo {
isBot := false
ui := &bridgev2.UserInfo{
IsBot: &isBot,
}
// TODO use template for name
if contact.ContactName != "" {
ui.Name = &contact.ContactName
} else if contact.Profile.Name != "" {
ui.Name = &contact.Profile.Name
} else if contact.E164 != "" {
ui.Name = &contact.E164
}
// TODO only use this if contact avatars are allowed
if contact.ContactAvatar.Hash != "" {
ui.Avatar = &bridgev2.Avatar{
ID: networkid.AvatarID("hash:" + contact.ContactAvatar.Hash),
Get: func(ctx context.Context) ([]byte, error) {
if contact.ContactAvatar.Image == nil {
return nil, fmt.Errorf("contact avatar not available")
}
return contact.ContactAvatar.Image, nil
},
}
} else if contact.Profile.AvatarPath != "" {
ui.Avatar = &bridgev2.Avatar{
ID: makeAvatarPathID(contact.Profile.AvatarPath),
Get: func(ctx context.Context) ([]byte, error) {
return s.Client.DownloadUserAvatar(ctx, contact.Profile.AvatarPath, contact.Profile.Key)
},
}
} else {
ui.Avatar = &bridgev2.Avatar{
ID: "",
Remove: true,
}
}
return ui
}

func (s *SignalClient) GetUserInfo(ctx context.Context, ghost *bridgev2.Ghost) (*bridgev2.UserInfo, error) {
userID, err := parseUserID(ghost.ID)
if err != nil {
return nil, err
}
contact, err := s.Client.ContactByACI(ctx, userID)
if err != nil {
return nil, err
}
return s.contactToUserInfo(contact), nil
}

func makeAvatarPathID(avatarPath string) networkid.AvatarID {
if avatarPath == "" {
return ""
}
return networkid.AvatarID("path:" + avatarPath)
}

func (s *SignalClient) GetChatInfo(ctx context.Context, portal *bridgev2.Portal) (*bridgev2.PortalInfo, error) {
return &bridgev2.PortalInfo{}, nil
userID, groupID, err := s.parsePortalID(portal.ID)
if err != nil {
return nil, err
}
isSpace := false
if groupID != "" {
groupInfo, err := s.Client.RetrieveGroupByID(ctx, groupID, 0)
if err != nil {
return nil, err
}
isDM := false
members := make([]networkid.UserID, len(groupInfo.Members))
for i, member := range groupInfo.Members {
members[i] = makeUserID(member.ACI)
}
return &bridgev2.PortalInfo{
Name: &groupInfo.Title,
Topic: &groupInfo.Description,
Avatar: &bridgev2.Avatar{
ID: makeAvatarPathID(groupInfo.AvatarPath),
Get: func(ctx context.Context) ([]byte, error) {
return s.Client.DownloadGroupAvatar(ctx, groupInfo)
},
Remove: groupInfo.AvatarPath == "",
},
Members: members,
IsDirectChat: &isDM,
IsSpace: &isSpace,
}, nil
} else if userID.Type == libsignalgo.ServiceIDTypePNI {
isDM := true
// TODO set name/avatar because we don't have the recipient user ID
return &bridgev2.PortalInfo{
Members: []networkid.UserID{makeUserID(s.Client.Store.ACI)},
IsDirectChat: &isDM,
IsSpace: &isSpace,
}, nil
} else {
isDM := true
return &bridgev2.PortalInfo{
Members: []networkid.UserID{makeUserID(userID.UUID), makeUserID(s.Client.Store.ACI)},
IsDirectChat: &isDM,
IsSpace: &isSpace,
}, nil
}
}

func (s *SignalClient) IsThisUser(_ context.Context, userID networkid.UserID) bool {
return userID == makeUserID(s.Client.Store.ACI)
}

func (s *SignalClient) Connect(ctx context.Context) error {
Expand All @@ -162,24 +269,31 @@ func (s *SignalClient) IsLoggedIn() bool {
return s.Client.IsLoggedIn()
}

func (s *SignalClient) parsePortalID(portalID networkid.PortalID) (string, error) {
func parseUserID(userID networkid.UserID) (uuid.UUID, error) {
return uuid.Parse(string(userID))
}

func (s *SignalClient) parsePortalID(portalID networkid.PortalID) (userID libsignalgo.ServiceID, groupID types.GroupIdentifier, err error) {
parts := strings.Split(string(portalID), "|")
if len(parts) == 1 {
if len(parts[0]) == 44 {
return parts[0], nil
groupID = types.GroupIdentifier(parts[0])
} else {
err = fmt.Errorf("invalid portal ID: expected group ID to be 44 characters")
}
return "", fmt.Errorf("invalid portal ID: expected group ID to be 44 characters")
} else if len(parts) == 2 {
ourACI := s.Client.Store.ACI.String()
if parts[0] == ourACI {
return parts[1], nil
userID, err = libsignalgo.ServiceIDFromString(parts[1])
} else if parts[1] == ourACI {
return parts[0], nil
userID, err = libsignalgo.ServiceIDFromString(parts[0])
} else {
return "", fmt.Errorf("invalid portal ID: expected one side to be our ACI")
err = fmt.Errorf("invalid portal ID: expected one side to be our ACI")
}
} else {
err = fmt.Errorf("invalid portal ID: unexpected number of pipe-separated parts")
}
return "", fmt.Errorf("invalid portal ID: unexpected number of pipe-separated parts")
return
}

func (s *SignalClient) getPortalID(chatID string) networkid.PortalID {
Expand Down Expand Up @@ -237,6 +351,13 @@ type msgconvContext struct {
}

func (s *SignalClient) convertMessage(ctx context.Context, portal *bridgev2.Portal, data *events.ChatEvent) (*bridgev2.ConvertedMessage, error) {
mcCtx := &msgconvContext{
Connector: s.Main,
Intent: s.Main.Bridge.Bot, // TODO get correct intent?
Client: s,
Portal: portal,
}
ctx = context.WithValue(ctx, msgconvContextKey, mcCtx)
dataMsg := data.Event.(*signalpb.DataMessage)
converted := s.Main.MsgConv.ToMatrix(ctx, dataMsg)
var replyTo *networkid.MessageOptionalPartID
Expand Down Expand Up @@ -304,20 +425,10 @@ func (s *SignalClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2.Ma
ReplyTo: msg.ReplyTo,
}
ctx = context.WithValue(ctx, msgconvContextKey, mcCtx)
chatID, err := s.parsePortalID(msg.Portal.ID)
userID, groupID, err := s.parsePortalID(msg.Portal.ID)
if err != nil {
return nil, err
}
var userID libsignalgo.ServiceID
var groupID types.GroupIdentifier
if len(chatID) == 44 {
groupID = types.GroupIdentifier(chatID)
} else {
userID, err = libsignalgo.ServiceIDFromString(chatID)
if err != nil {
return nil, err
}
}
converted, err := s.Main.MsgConv.ToSignal(ctx, msg.Event, msg.Content, msg.OrigSender != nil)
if err != nil {
return nil, err
Expand Down Expand Up @@ -414,7 +525,11 @@ func (mpm *msgconvPortalMethods) GetClient(ctx context.Context) *signalmeow.Clie
func (mpm *msgconvPortalMethods) GetData(ctx context.Context) *legacydb.Portal {
mcCtx := ctx.Value(msgconvContextKey).(*msgconvContext)
portal := mcCtx.Portal
chatID, _ := mcCtx.Client.parsePortalID(portal.ID)
userID, groupID, _ := mcCtx.Client.parsePortalID(portal.ID)
chatID := string(groupID)
if chatID == "" {
chatID = userID.String()
}
pk := legacydb.PortalKey{
ChatID: chatID,
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ require (
golang.org/x/net v0.25.0
google.golang.org/protobuf v1.34.1
gopkg.in/yaml.v3 v3.0.1
maunium.net/go/mautrix v0.18.2-0.20240527105318-9254a5d6c1d9
maunium.net/go/mautrix v0.18.2-0.20240528174923-3c7b3e13efe4
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 @@ -95,7 +95,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.18.2-0.20240527105318-9254a5d6c1d9 h1:pRnJkxSjLsJSWON0yWzHNDyQqeebvgCeRXzozntEj/0=
maunium.net/go/mautrix v0.18.2-0.20240527105318-9254a5d6c1d9/go.mod h1:Ln4XquIKL5MttTUGNUSbiEGX3XYC0P6jzT9XjLFFPdY=
maunium.net/go/mautrix v0.18.2-0.20240528174923-3c7b3e13efe4 h1:b+pK+EJL2XsweSKlwhd8eeqD4v395dKrcJZmCFTBmBY=
maunium.net/go/mautrix v0.18.2-0.20240528174923-3c7b3e13efe4/go.mod h1:Ln4XquIKL5MttTUGNUSbiEGX3XYC0P6jzT9XjLFFPdY=
nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0=
nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
15 changes: 8 additions & 7 deletions portal.go
Original file line number Diff line number Diff line change
Expand Up @@ -1765,7 +1765,7 @@ func (portal *Portal) CreateMatrixRoom(ctx context.Context, user *User, groupRev
initialState = append(initialState, &event.Event{
Type: event.StateRoomAvatar,
Content: event.Content{Parsed: &event.RoomAvatarEventContent{
URL: portal.AvatarURL,
URL: portal.AvatarURL.CUString(),
}},
})
}
Expand Down Expand Up @@ -2910,17 +2910,18 @@ func (portal *Portal) HandleMatrixMeta(brSender bridge.User, evt *event.Event) {
portal.Topic = content.Topic
groupChange.ModifyDescription = &content.Topic
case *event.RoomAvatarEventContent:
if content.URL == portal.AvatarURL {
url := content.URL.ParseOrIgnore()
if url == portal.AvatarURL {
return
}
var data []byte
if !content.URL.IsEmpty() {
data, err = portal.MainIntent().DownloadBytes(ctx, content.URL)
if !url.IsEmpty() {
data, err = portal.MainIntent().DownloadBytes(ctx, url)
if err != nil {
log.Err(err).Stringer("Failed to download updated avatar %s", content.URL)
log.Err(err).Stringer("Failed to download updated avatar %s", url)
return
}
log.Debug().Stringers("%s set the group avatar to %s", []fmt.Stringer{sender.MXID, content.URL})
log.Debug().Stringers("%s set the group avatar to %s", []fmt.Stringer{sender.MXID, url})
} else {
log.Debug().Stringer("%s removed the group avatar", sender.MXID)
}
Expand All @@ -2933,7 +2934,7 @@ func (portal *Portal) HandleMatrixMeta(brSender bridge.User, evt *event.Event) {
hash := sha256.Sum256(data)
avatarHash = hex.EncodeToString(hash[:])
avatarChanged = true
avatarURL = content.URL
avatarURL = url
}
revision, err := sender.Client.UpdateGroup(ctx, groupChange, portal.GroupID())
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion user.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ func (user *User) GetSpaceRoom(ctx context.Context) id.RoomID {
Type: event.StateRoomAvatar,
Content: event.Content{
Parsed: &event.RoomAvatarEventContent{
URL: user.bridge.Config.AppService.Bot.ParsedAvatar,
URL: user.bridge.Config.AppService.Bot.ParsedAvatar.CUString(),
},
},
}},
Expand Down

0 comments on commit e5e3dd1

Please sign in to comment.