From 823eda7589d88af1413b21b293ce4027b4367100 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 24 Jan 2025 15:34:34 +0200 Subject: [PATCH] push: implement parsing native notifications (#87) --- go.mod | 12 ++++---- go.sum | 19 ++++++------ pkg/connector/push.go | 70 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 85 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 2e6d7685..edaf3980 100644 --- a/go.mod +++ b/go.mod @@ -10,12 +10,13 @@ require ( github.com/gotd/td v0.111.0 github.com/rs/zerolog v1.33.0 github.com/stretchr/testify v1.10.0 - go.mau.fi/util v0.8.4-0.20250110124612-64d4dbbec957 + github.com/tidwall/gjson v1.18.0 + go.mau.fi/util v0.8.4 go.mau.fi/zerozap v0.1.1 go.uber.org/zap v1.27.0 - golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 + golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 golang.org/x/net v0.34.0 - maunium.net/go/mautrix v0.22.2-0.20250110154103-bbcb1904e268 + maunium.net/go/mautrix v0.23.1-0.20250122131841-524379bdb327 ) require ( @@ -31,7 +32,7 @@ require ( github.com/gotd/neo v0.1.5 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/lib/pq v1.10.9 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.24 // indirect github.com/petermattis/goid v0.0.0-20241211131331-93ee7e083c43 // indirect @@ -39,7 +40,6 @@ require ( github.com/rs/xid v1.6.0 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect - github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect @@ -59,4 +59,4 @@ require ( rsc.io/qr v0.2.0 // indirect ) -replace github.com/gotd/td => github.com/beeper/td v0.107.1-0.20241219153009-6e671c4e86b0 +replace github.com/gotd/td => github.com/beeper/td v0.107.1-0.20250122125324-9af21bf4109e diff --git a/go.sum b/go.sum index 5975db60..a37cb31d 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= -github.com/beeper/td v0.107.1-0.20241219153009-6e671c4e86b0 h1:+bLLCnRdnntmNGLvxigSlDPUOJlsqHw6cxTgNTGMGW4= -github.com/beeper/td v0.107.1-0.20241219153009-6e671c4e86b0/go.mod h1:5Db4K0d5B+vGIBpNVv2F0ABM14f0PcRJ+RQlKRnUcZQ= +github.com/beeper/td v0.107.1-0.20250122125324-9af21bf4109e h1:8qzwKf37vqg1rO6tPIWybxCXUygOGFjl+TA9rZrCDFE= +github.com/beeper/td v0.107.1-0.20250122125324-9af21bf4109e/go.mod h1:5Db4K0d5B+vGIBpNVv2F0ABM14f0PcRJ+RQlKRnUcZQ= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= @@ -38,8 +38,9 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -76,8 +77,8 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -go.mau.fi/util v0.8.4-0.20250110124612-64d4dbbec957 h1:tsLt3t6ARc55niz+JMgJy6U4sL210Z0K/nyxF09xT0E= -go.mau.fi/util v0.8.4-0.20250110124612-64d4dbbec957/go.mod h1:MOfGTs1CBuK6ERTcSL4lb5YU7/ujz09eOPVEDckuazY= +go.mau.fi/util v0.8.4 h1:mVKlJcXWfVo8ZW3f4vqtjGpqtZqJvX4ETekxawt2vnQ= +go.mau.fi/util v0.8.4/go.mod h1:MOfGTs1CBuK6ERTcSL4lb5YU7/ujz09eOPVEDckuazY= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= go.mau.fi/zerozap v0.1.1 h1:mxE/dW4wtkqBYOXOEEzXldk5qKB+ahsZXjoTGnvEhZQ= @@ -96,8 +97,8 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 h1:9kj3STMvgqy3YA4VQXBrN7925ICMxD5wzMRcgA30588= -golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -119,8 +120,8 @@ 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.22.2-0.20250110154103-bbcb1904e268 h1:p+3TofdhqiVYIkLjgzidayg2XriGUEbj+nbWs3/UQbk= -maunium.net/go/mautrix v0.22.2-0.20250110154103-bbcb1904e268/go.mod h1:07i96D7BALyuAqxFhRzvaId8FC9NABgRQBPY5HWndf4= +maunium.net/go/mautrix v0.23.1-0.20250122131841-524379bdb327 h1:Fb1bAUNz3eEYI6jegWv1OLwWJQRy8TzvlrvZfS4qc2U= +maunium.net/go/mautrix v0.23.1-0.20250122131841-524379bdb327/go.mod h1:AGnnaz3ylGikUo1I1MJVn9QLsl2No1/ZNnGDyO0QD5s= nhooyr.io/websocket v1.8.17 h1:KEVeLJkUywCKVsnLIDlD/5gtayKp8VoCkksHCGGfT9Y= nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= diff --git a/pkg/connector/push.go b/pkg/connector/push.go index 8791f085..919d9ef4 100644 --- a/pkg/connector/push.go +++ b/pkg/connector/push.go @@ -2,17 +2,85 @@ package connector import ( "context" + "crypto/rand" + "encoding/base64" + "encoding/json" "fmt" + "github.com/gotd/td/bin" + "github.com/gotd/td/crypto" "github.com/gotd/td/tg" + "github.com/tidwall/gjson" "go.mau.fi/util/random" "maunium.net/go/mautrix/bridgev2" + "maunium.net/go/mautrix/bridgev2/networkid" ) -var _ bridgev2.PushableNetworkAPI = (*TelegramClient)(nil) +var ( + _ bridgev2.PushableNetworkAPI = (*TelegramClient)(nil) + _ bridgev2.PushParsingNetwork = (*TelegramConnector)(nil) +) var PushAppSandbox = false +// TODO +type PushNotificationData map[string]any + +func (tg *TelegramConnector) ParsePushNotification(ctx context.Context, data json.RawMessage) (networkid.UserLoginID, any, error) { + val := gjson.GetBytes(data, "p") + if val.Type != gjson.String { + return "", nil, fmt.Errorf("missing or invalid p field") + } + valBytes, err := base64.RawURLEncoding.DecodeString(val.Str) + if err != nil { + return "", nil, fmt.Errorf("failed to base64 decode p field: %w", err) + } + var em crypto.EncryptedMessage + err = em.DecodeWithoutCopy(&bin.Buffer{Buf: valBytes}) + if err != nil { + return "", nil, fmt.Errorf("failed to decode auth key and message ID: %w", err) + } + userIDs, err := tg.Bridge.DB.UserLogin.GetAllUserIDsWithLogins(ctx) + if err != nil { + return "", nil, fmt.Errorf("failed to get users with logins: %w", err) + } + var matchingAuthKey *crypto.AuthKey + var userLoginID networkid.UserLoginID +UserLoop: + for _, userID := range userIDs { + user, err := tg.Bridge.GetExistingUserByMXID(ctx, userID) + if err != nil { + return "", nil, fmt.Errorf("failed to get user %s: %w", userID, err) + } + for _, login := range user.GetUserLogins() { + key := login.Metadata.(*UserLoginMetadata).PushEncryptionKey + if len(key) != 256 { + continue + } + authKey := crypto.Key(key).WithID() + if authKey.ID == em.AuthKeyID { + matchingAuthKey = &authKey + userLoginID = login.ID + break UserLoop + } + } + } + if matchingAuthKey == nil { + return "", nil, fmt.Errorf("no matching auth key found") + } + c := crypto.NewClientCipher(rand.Reader) + plaintext, err := c.DecryptRaw(*matchingAuthKey, &em) + if err != nil { + return userLoginID, nil, fmt.Errorf("failed to decrypt payload: %w", err) + } + pmd := make(PushNotificationData) + err = json.Unmarshal(plaintext, &pmd) + if err != nil { + return userLoginID, nil, fmt.Errorf("failed to unmarshal decrypted payload: %w", err) + } + return userLoginID, pmd, nil +} + func (t *TelegramClient) RegisterPushNotifications(ctx context.Context, pushType bridgev2.PushType, token string) error { meta := t.userLogin.Metadata.(*UserLoginMetadata) if meta.PushEncryptionKey == nil {