From 74e5984388a873497aa171de1b8a3d8b82f4c8fc Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 20 May 2024 11:52:19 +0300 Subject: [PATCH] Add proxy options to config --- config/config.go | 4 ++++ config/upgrade.go | 3 +++ example-config.yaml | 6 ++++++ go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- user.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 73 insertions(+), 15 deletions(-) diff --git a/config/config.go b/config/config.go index 69dbd2c7..4069195a 100644 --- a/config/config.go +++ b/config/config.go @@ -38,6 +38,10 @@ type Config struct { WhatsApp struct { OSName string `yaml:"os_name"` BrowserName string `yaml:"browser_name"` + + Proxy string `yaml:"proxy"` + GetProxyURL string `yaml:"get_proxy_url"` + ProxyOnlyLogin bool `yaml:"proxy_only_login"` } `yaml:"whatsapp"` Bridge BridgeConfig `yaml:"bridge"` diff --git a/config/upgrade.go b/config/upgrade.go index 48d728ca..b7917c74 100644 --- a/config/upgrade.go +++ b/config/upgrade.go @@ -36,6 +36,9 @@ func DoUpgrade(helper *up.Helper) { helper.Copy(up.Str, "whatsapp", "os_name") helper.Copy(up.Str, "whatsapp", "browser_name") + helper.Copy(up.Str|up.Null, "whatsapp", "proxy") + helper.Copy(up.Str|up.Null, "whatsapp", "get_proxy_url") + helper.Copy(up.Bool, "whatsapp", "proxy_only_login") helper.Copy(up.Str, "bridge", "username_template") helper.Copy(up.Str, "bridge", "displayname_template") diff --git a/example-config.yaml b/example-config.yaml index 1acd68e8..25ddf940 100644 --- a/example-config.yaml +++ b/example-config.yaml @@ -100,6 +100,12 @@ whatsapp: # Must be "unknown" for a generic icon or a valid browser name if you want a specific icon. # List of valid browser names: https://github.com/tulir/whatsmeow/blob/efc632c008604016ddde63bfcfca8de4e5304da9/binary/proto/def.proto#L43-L64 browser_name: unknown + # Proxy to use for all WhatsApp connections. + proxy: null + # Alternative to proxy: an HTTP endpoint that returns the proxy URL to use for WhatsApp connections. + get_proxy_url: null + # Whether the proxy options should only apply to the login websocket and not to authenticated connections. + proxy_only_login: false # Bridge config bridge: diff --git a/go.mod b/go.mod index 19b01789..49080df2 100644 --- a/go.mod +++ b/go.mod @@ -14,10 +14,10 @@ require ( github.com/tidwall/gjson v1.17.1 go.mau.fi/util v0.4.2 go.mau.fi/webp v0.1.0 - go.mau.fi/whatsmeow v0.0.0-20240327124018-350073db195c + go.mau.fi/whatsmeow v0.0.0-20240520084720-d900d6f2af0f golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 golang.org/x/image v0.15.0 - golang.org/x/net v0.24.0 + golang.org/x/net v0.25.0 golang.org/x/sync v0.7.0 google.golang.org/protobuf v1.33.0 maunium.net/go/mautrix v0.18.1 @@ -42,9 +42,9 @@ require ( github.com/yuin/goldmark v1.7.1 // indirect go.mau.fi/libsignal v0.1.0 // indirect go.mau.fi/zeroconfig v0.1.2 // indirect - golang.org/x/crypto v0.22.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect maunium.net/go/mauflag v1.0.0 // indirect diff --git a/go.sum b/go.sum index e30451d7..b278fb72 100644 --- a/go.sum +++ b/go.sum @@ -73,27 +73,27 @@ go.mau.fi/util v0.4.2 h1:RR3TOcRHmCF9Bx/3YG4S65MYfa+nV6/rn8qBWW4Mi30= go.mau.fi/util v0.4.2/go.mod h1:PlAVfUUcPyHPrwnvjkJM9UFcPE7qGPDJqk+Oufa1Gtw= go.mau.fi/webp v0.1.0 h1:BHObH/DcFntT9KYun5pDr0Ot4eUZO8k2C7eP7vF4ueA= go.mau.fi/webp v0.1.0/go.mod h1:e42Z+VMFrUMS9cpEwGRIor+lQWO8oUAyPyMtcL+NMt8= -go.mau.fi/whatsmeow v0.0.0-20240327124018-350073db195c h1:a5O4nqmwUWvmC+27RUdefkuy5XzMOEUqR9ji+/BcHZA= -go.mau.fi/whatsmeow v0.0.0-20240327124018-350073db195c/go.mod h1:kNI5foyzqd77d5HaWc1Jico6/rxtZ/UE8nr80hIsbIk= +go.mau.fi/whatsmeow v0.0.0-20240520084720-d900d6f2af0f h1:ZqNMbb/XIAANkOn/c0FzFjm75pbTtNYyide3jGl2ptQ= +go.mau.fi/whatsmeow v0.0.0-20240520084720-d900d6f2af0f/go.mod h1:0+65CYaE6r4dWzr0dN8i+UZKy0gIfJ79VuSqIl0nKRM= go.mau.fi/zeroconfig v0.1.2 h1:DKOydWnhPMn65GbXZOafgkPm11BvFashZWLct0dGFto= go.mau.fi/zeroconfig v0.1.2/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc= golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/user.go b/user.go index a10be81b..6f8c5ae2 100644 --- a/user.go +++ b/user.go @@ -27,6 +27,7 @@ import ( "math" "math/rand" "net/http" + "net/url" "strconv" "strings" "sync" @@ -556,6 +557,50 @@ func (user *User) createClient(sess *store.Device) { user.bridge.Metrics.TrackRetryReceipt(retryCount, true) return true } + if !user.bridge.Config.WhatsApp.ProxyOnlyLogin || sess.ID == nil { + if proxy, err := user.getProxy("login"); err != nil { + user.zlog.Err(err).Msg("Failed to get proxy address") + } else if err = user.Client.SetProxyAddress(proxy); err != nil { + user.zlog.Err(err).Msg("Failed to set proxy address") + } + } + if user.bridge.Config.WhatsApp.ProxyOnlyLogin { + user.Client.ToggleProxyOnlyForLogin(true) + } +} + +type respGetProxy struct { + ProxyURL string `json:"proxy_url"` +} + +func (user *User) getProxy(reason string) (string, error) { + if user.bridge.Config.WhatsApp.GetProxyURL == "" { + return user.bridge.Config.WhatsApp.Proxy, nil + } + parsed, err := url.Parse(user.bridge.Config.WhatsApp.GetProxyURL) + if err != nil { + return "", fmt.Errorf("failed to parse address: %w", err) + } + q := parsed.Query() + q.Set("reason", reason) + parsed.RawQuery = q.Encode() + req, err := http.NewRequest(http.MethodGet, parsed.String(), nil) + if err != nil { + return "", fmt.Errorf("failed to prepare request: %w", err) + } + req.Header.Set("User-Agent", mautrix.DefaultUserAgent) + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", fmt.Errorf("failed to send request: %w", err) + } else if resp.StatusCode >= 300 || resp.StatusCode < 200 { + return "", fmt.Errorf("unexpected status code %d", resp.StatusCode) + } + var respData respGetProxy + err = json.NewDecoder(resp.Body).Decode(&respData) + if err != nil { + return "", fmt.Errorf("failed to decode response: %w", err) + } + return respData.ProxyURL, nil } func (user *User) Login(ctx context.Context) (<-chan whatsmeow.QRChannelItem, error) {