fix issue 1936

This commit is contained in:
EndlessLucky 2026-03-31 02:10:47 -04:00
parent c36b06a901
commit 3896b73b74
2 changed files with 78 additions and 12 deletions

View file

@ -3,12 +3,15 @@ package telegram
import ( import (
"context" "context"
"crypto/rand" "crypto/rand"
"crypto/tls"
"crypto/x509"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"path/filepath"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@ -59,23 +62,20 @@ func NewTelegramChannel(cfg *config.Config, bus *bus.MessageBus) (*TelegramChann
var opts []telego.BotOption var opts []telego.BotOption
telegramCfg := cfg.Channels.Telegram telegramCfg := cfg.Channels.Telegram
transport, err := telegramHTTPTransport(telegramCfg)
if err != nil {
return nil, err
}
opts = append(opts, telego.WithHTTPClient(&http.Client{Transport: transport}))
if telegramCfg.Proxy != "" { if telegramCfg.Proxy != "" {
proxyURL, parseErr := url.Parse(telegramCfg.Proxy) proxyURL, parseErr := url.Parse(telegramCfg.Proxy)
if parseErr != nil { if parseErr != nil {
return nil, fmt.Errorf("invalid proxy URL %q: %w", telegramCfg.Proxy, parseErr) return nil, fmt.Errorf("invalid proxy URL %q: %w", telegramCfg.Proxy, parseErr)
} }
opts = append(opts, telego.WithHTTPClient(&http.Client{ transport.Proxy = http.ProxyURL(proxyURL)
Transport: &http.Transport{ } else {
Proxy: http.ProxyURL(proxyURL), transport.Proxy = http.ProxyFromEnvironment
},
}))
} else if os.Getenv("HTTP_PROXY") != "" || os.Getenv("HTTPS_PROXY") != "" {
// Use environment proxy if configured
opts = append(opts, telego.WithHTTPClient(&http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
},
}))
} }
if baseURL := strings.TrimRight(strings.TrimSpace(telegramCfg.BaseURL), "/"); baseURL != "" { if baseURL := strings.TrimRight(strings.TrimSpace(telegramCfg.BaseURL), "/"); baseURL != "" {
@ -106,6 +106,69 @@ func NewTelegramChannel(cfg *config.Config, bus *bus.MessageBus) (*TelegramChann
}, nil }, nil
} }
func telegramHTTPTransport(cfg config.TelegramConfig) (*http.Transport, error) {
// Start with Go defaults, then inject proxy + TLS tweaks.
// We keep timeouts conservative; telego itself also applies request-level timeouts.
t := &http.Transport{
Proxy: http.ProxyFromEnvironment,
}
tlsCfg, err := telegramTLSConfig(cfg)
if err != nil {
return nil, err
}
if tlsCfg != nil {
t.TLSClientConfig = tlsCfg
}
return t, nil
}
func telegramTLSConfig(cfg config.TelegramConfig) (*tls.Config, error) {
if !cfg.TLSInsecure && strings.TrimSpace(cfg.TLSCAFile) == "" && strings.TrimSpace(cfg.TLSCADir) == "" {
return nil, nil
}
base, err := x509.SystemCertPool()
if err != nil || base == nil {
base = x509.NewCertPool()
}
if caFile := strings.TrimSpace(cfg.TLSCAFile); caFile != "" {
pemBytes, readErr := os.ReadFile(caFile)
if readErr != nil {
return nil, fmt.Errorf("telegram tls_ca_file read %q: %w", caFile, readErr)
}
if ok := base.AppendCertsFromPEM(pemBytes); !ok {
return nil, fmt.Errorf("telegram tls_ca_file %q: no certificates found", caFile)
}
}
if caDir := strings.TrimSpace(cfg.TLSCADir); caDir != "" {
entries, readErr := os.ReadDir(caDir)
if readErr != nil {
return nil, fmt.Errorf("telegram tls_ca_dir read %q: %w", caDir, readErr)
}
for _, ent := range entries {
if ent.IsDir() {
continue
}
// Common practice: read every file; ignore parse failures quietly to handle
// hashed symlinks / non-PEM files.
p := filepath.Join(caDir, ent.Name())
b, e := os.ReadFile(p)
if e != nil {
continue
}
_ = base.AppendCertsFromPEM(b)
}
}
return &tls.Config{
RootCAs: base,
InsecureSkipVerify: cfg.TLSInsecure,
}, nil
}
func (c *TelegramChannel) Start(ctx context.Context) error { func (c *TelegramChannel) Start(ctx context.Context) error {
logger.InfoC("telegram", "Starting Telegram bot (polling mode)...") logger.InfoC("telegram", "Starting Telegram bot (polling mode)...")

View file

@ -344,6 +344,9 @@ type TelegramConfig struct {
Token SecureString `json:"token,omitzero" yaml:"token,omitempty" env:"PICOCLAW_CHANNELS_TELEGRAM_TOKEN"` Token SecureString `json:"token,omitzero" yaml:"token,omitempty" env:"PICOCLAW_CHANNELS_TELEGRAM_TOKEN"`
BaseURL string `json:"base_url" yaml:"-" env:"PICOCLAW_CHANNELS_TELEGRAM_BASE_URL"` BaseURL string `json:"base_url" yaml:"-" env:"PICOCLAW_CHANNELS_TELEGRAM_BASE_URL"`
Proxy string `json:"proxy" yaml:"-" env:"PICOCLAW_CHANNELS_TELEGRAM_PROXY"` Proxy string `json:"proxy" yaml:"-" env:"PICOCLAW_CHANNELS_TELEGRAM_PROXY"`
TLSCAFile string `json:"tls_ca_file,omitempty" yaml:"-" env:"PICOCLAW_CHANNELS_TELEGRAM_TLS_CA_FILE"`
TLSCADir string `json:"tls_ca_dir,omitempty" yaml:"-" env:"PICOCLAW_CHANNELS_TELEGRAM_TLS_CA_DIR"`
TLSInsecure bool `json:"tls_insecure,omitempty" yaml:"-" env:"PICOCLAW_CHANNELS_TELEGRAM_TLS_INSECURE"`
AllowFrom FlexibleStringSlice `json:"allow_from" yaml:"-" env:"PICOCLAW_CHANNELS_TELEGRAM_ALLOW_FROM"` AllowFrom FlexibleStringSlice `json:"allow_from" yaml:"-" env:"PICOCLAW_CHANNELS_TELEGRAM_ALLOW_FROM"`
GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty" yaml:"-"` GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty" yaml:"-"`
Typing TypingConfig `json:"typing,omitempty" yaml:"-"` Typing TypingConfig `json:"typing,omitempty" yaml:"-"`