diff --git a/go.mod b/go.mod index 5a418be3..c5862b48 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 golang.org/x/net v0.26.0 google.golang.org/protobuf v1.34.2 - maunium.net/go/mautrix v0.19.0-beta.1.0.20240620135116-0418273bdbb1 + maunium.net/go/mautrix v0.19.0-beta.1.0.20240620142853-7b6f3ba0541d nhooyr.io/websocket v1.8.11 ) diff --git a/go.sum b/go.sum index fe0d52e4..bbb191e3 100644 --- a/go.sum +++ b/go.sum @@ -93,7 +93,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.0-beta.1.0.20240620135116-0418273bdbb1 h1:sSz/VCo3GLtnAjMBpjfn7dtN1f7RDdZ+9OTZdaxZSJc= -maunium.net/go/mautrix v0.19.0-beta.1.0.20240620135116-0418273bdbb1/go.mod h1:cxv1w6+syudmEpOewHYIQT9yO7TM5UOWmf6xEBVI4H4= +maunium.net/go/mautrix v0.19.0-beta.1.0.20240620142853-7b6f3ba0541d h1:s3wbfQ3jJOlZy0oYE/dVSTxAHDMQHwBlHVLqQZGeQX4= +maunium.net/go/mautrix v0.19.0-beta.1.0.20240620142853-7b6f3ba0541d/go.mod h1:cxv1w6+syudmEpOewHYIQT9yO7TM5UOWmf6xEBVI4H4= nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0= nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index ceb2a86e..7c4c6466 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -91,7 +91,11 @@ func (s *SignalClient) GetChatInfo(ctx context.Context, portal *bridgev2.Portal) func (s *SignalClient) contactToUserInfo(contact *types.Recipient) *bridgev2.UserInfo { isBot := false ui := &bridgev2.UserInfo{ - IsBot: &isBot, + IsBot: &isBot, + Identifiers: []string{}, + } + if contact.E164 != "" { + ui.Identifiers = append(ui.Identifiers, "tel:"+contact.E164) } name := s.Main.Config.FormatDisplayname(contact) ui.Name = &name @@ -184,6 +188,32 @@ func (s *SignalClient) CreateGroup(ctx context.Context, name string, users ...ne return nil, fmt.Errorf("not implemented") } +func (s *SignalClient) GetContactList(ctx context.Context) ([]*bridgev2.ResolveIdentifierResponse, error) { + recipients, err := s.Client.Store.RecipientStore.LoadAllContacts(ctx) + if err != nil { + return nil, err + } + resp := make([]*bridgev2.ResolveIdentifierResponse, len(recipients)) + for i, recipient := range recipients { + recipientResp := &bridgev2.ResolveIdentifierResponse{ + UserInfo: s.contactToUserInfo(recipient), + Chat: s.makeCreateDMResponse(recipient), + } + if recipient.ACI != uuid.Nil { + recipientResp.UserID = makeUserID(recipient.ACI) + ghost, err := s.Main.Bridge.GetGhostByID(ctx, recipientResp.UserID) + if err != nil { + return nil, fmt.Errorf("failed to get ghost for %s: %w", recipient.ACI, err) + } + recipientResp.Ghost = ghost + } else { + recipientResp.UserID = makeUserIDFromServiceID(libsignalgo.NewPNIServiceID(recipient.PNI)) + } + resp[i] = recipientResp + } + return resp, nil +} + func (s *SignalClient) makeCreateDMResponse(recipient *types.Recipient) *bridgev2.CreateChatResponse { isDirectChat := true isSpace := false diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 6034a7c4..20f3d8e2 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -21,7 +21,9 @@ type SignalClient struct { var _ bridgev2.NetworkAPI = (*SignalClient)(nil) var _ bridgev2.PushableNetworkAPI = (*SignalClient)(nil) +var _ bridgev2.IdentifierResolvingNetworkAPI = (*SignalClient)(nil) var _ bridgev2.GroupCreatingNetworkAPI = (*SignalClient)(nil) +var _ bridgev2.ContactListingNetworkAPI = (*SignalClient)(nil) var pushCfg = &bridgev2.PushConfig{ FCM: &bridgev2.FCMPushConfig{ diff --git a/pkg/signalmeow/store/recipient_store.go b/pkg/signalmeow/store/recipient_store.go index 216dfc89..3463a004 100644 --- a/pkg/signalmeow/store/recipient_store.go +++ b/pkg/signalmeow/store/recipient_store.go @@ -40,6 +40,8 @@ type RecipientStore interface { LoadRecipientByE164(ctx context.Context, e164 string) (*types.Recipient, error) StoreRecipient(ctx context.Context, recipient *types.Recipient) error UpdateRecipientE164(ctx context.Context, aci, pni uuid.UUID, e164 string) (*types.Recipient, error) + + LoadAllContacts(ctx context.Context) ([]*types.Recipient, error) } var _ RecipientStore = (*sqlStore)(nil) @@ -62,12 +64,13 @@ const ( FROM signalmeow_recipients WHERE account_id = $1 ` - getRecipientByACIQuery = getAllRecipientsQuery + `AND aci_uuid = $2` - getRecipientByPNIQuery = getAllRecipientsQuery + `AND pni_uuid = $2` - getRecipientByACIOrPNIQuery = getAllRecipientsQuery + `AND (($2<>'00000000-0000-0000-0000-000000000000' AND aci_uuid = $2) OR ($3<>'00000000-0000-0000-0000-000000000000' AND pni_uuid = $3))` - getRecipientByPhoneQuery = getAllRecipientsQuery + `AND e164_number = $2` - deleteRecipientByPNIQuery = `DELETE FROM signalmeow_recipients WHERE account_id = $1 AND pni_uuid = $2` - upsertACIRecipientQuery = ` + getAllRecipientsWithNameOrPhoneQuery = getAllRecipientsQuery + `AND (contact_name <> '' OR profile_name <> '' OR e164_number <> '')` + getRecipientByACIQuery = getAllRecipientsQuery + `AND aci_uuid = $2` + getRecipientByPNIQuery = getAllRecipientsQuery + `AND pni_uuid = $2` + getRecipientByACIOrPNIQuery = getAllRecipientsQuery + `AND (($2<>'00000000-0000-0000-0000-000000000000' AND aci_uuid = $2) OR ($3<>'00000000-0000-0000-0000-000000000000' AND pni_uuid = $3))` + getRecipientByPhoneQuery = getAllRecipientsQuery + `AND e164_number = $2` + deleteRecipientByPNIQuery = `DELETE FROM signalmeow_recipients WHERE account_id = $1 AND pni_uuid = $2` + upsertACIRecipientQuery = ` INSERT INTO signalmeow_recipients ( account_id, aci_uuid, @@ -278,6 +281,11 @@ func (s *sqlStore) LoadRecipientByE164(ctx context.Context, e164 string) (*types return scanRecipient(s.db.QueryRow(ctx, getRecipientByPhoneQuery, s.AccountID, e164)) } +func (s *sqlStore) LoadAllContacts(ctx context.Context) ([]*types.Recipient, error) { + rows, err := s.db.Query(ctx, getAllRecipientsWithNameOrPhoneQuery, s.AccountID) + return dbutil.NewRowIterWithError(rows, scanRecipient, err).AsList() +} + func (s *sqlStore) DeleteRecipientByPNI(ctx context.Context, pni uuid.UUID) error { _, err := s.db.Exec(ctx, deleteRecipientByPNIQuery, s.AccountID, pni) return err