diff --git a/config/config.example.json b/config/config.example.json index f0cce6d72..d3763e402 100644 --- a/config/config.example.json +++ b/config/config.example.json @@ -127,6 +127,7 @@ "whatsapp": { "enabled": false, "bridge_url": "ws://localhost:3001", + "proxy": "", "use_native": false, "session_store_path": "", "allow_from": [], diff --git a/pkg/channels/whatsapp/whatsapp.go b/pkg/channels/whatsapp/whatsapp.go index 98622fe37..67b02ecf0 100644 --- a/pkg/channels/whatsapp/whatsapp.go +++ b/pkg/channels/whatsapp/whatsapp.go @@ -4,6 +4,9 @@ import ( "context" "encoding/json" "fmt" + "net/http" + "net/url" + "os" "sync" "time" @@ -56,6 +59,16 @@ func (c *WhatsAppChannel) Start(ctx context.Context) error { dialer := websocket.DefaultDialer dialer.HandshakeTimeout = 10 * time.Second + if c.config.Proxy != "" { + proxyURL, parseErr := url.Parse(c.config.Proxy) + if parseErr != nil { + return fmt.Errorf("invalid proxy URL %q: %w", c.config.Proxy, parseErr) + } + dialer.Proxy = http.ProxyURL(proxyURL) + } else if os.Getenv("HTTP_PROXY") != "" || os.Getenv("HTTPS_PROXY") != "" { + dialer.Proxy = http.ProxyFromEnvironment + } + conn, resp, err := dialer.Dial(c.url, nil) if resp != nil { resp.Body.Close() diff --git a/pkg/channels/whatsapp_native/whatsapp_native.go b/pkg/channels/whatsapp_native/whatsapp_native.go index d0a74a405..c50276fe0 100644 --- a/pkg/channels/whatsapp_native/whatsapp_native.go +++ b/pkg/channels/whatsapp_native/whatsapp_native.go @@ -11,6 +11,8 @@ import ( "context" "database/sql" "fmt" + "net/http" + "net/url" "os" "path/filepath" "strings" @@ -124,6 +126,16 @@ func (c *WhatsAppNativeChannel) Start(ctx context.Context) error { client := whatsmeow.NewClient(deviceStore, waLogger) + if c.config.Proxy != "" { + proxyURL, parseErr := url.Parse(c.config.Proxy) + if parseErr != nil { + return fmt.Errorf("invalid proxy URL %q: %w", c.config.Proxy, parseErr) + } + client.SetProxy(http.ProxyURL(proxyURL)) + } else if os.Getenv("HTTP_PROXY") != "" || os.Getenv("HTTPS_PROXY") != "" { + client.SetProxy(http.ProxyFromEnvironment) + } + // Create runCtx/runCancel BEFORE registering event handler and starting // goroutines so that Stop() can cancel them at any time, including during // the QR-login flow. diff --git a/pkg/config/config.go b/pkg/config/config.go index 4e8733cbf..da8ed46d5 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -336,6 +336,7 @@ type StreamingConfig struct { type WhatsAppConfig struct { Enabled bool `json:"enabled" yaml:"-" env:"PICOCLAW_CHANNELS_WHATSAPP_ENABLED"` BridgeURL string `json:"bridge_url" yaml:"-" env:"PICOCLAW_CHANNELS_WHATSAPP_BRIDGE_URL"` + Proxy string `json:"proxy" yaml:"-" env:"PICOCLAW_CHANNELS_WHATSAPP_PROXY"` UseNative bool `json:"use_native" yaml:"-" env:"PICOCLAW_CHANNELS_WHATSAPP_USE_NATIVE"` SessionStorePath string `json:"session_store_path" yaml:"-" env:"PICOCLAW_CHANNELS_WHATSAPP_SESSION_STORE_PATH"` AllowFrom FlexibleStringSlice `json:"allow_from" yaml:"-" env:"PICOCLAW_CHANNELS_WHATSAPP_ALLOW_FROM"` diff --git a/pkg/migrate/sources/openclaw/openclaw_config.go b/pkg/migrate/sources/openclaw/openclaw_config.go index 4436c1861..d61b4f333 100644 --- a/pkg/migrate/sources/openclaw/openclaw_config.go +++ b/pkg/migrate/sources/openclaw/openclaw_config.go @@ -642,6 +642,7 @@ type ChannelsConfig struct { type WhatsAppConfig struct { Enabled bool `json:"enabled"` BridgeURL string `json:"bridge_url"` + Proxy string `json:"proxy"` AllowFrom []string `json:"allow_from"` }