From c47eef0d704db391d693b9da2cf113ac24a998bd Mon Sep 17 00:00:00 2001 From: divyam234 <47589864+divyam234@users.noreply.github.com> Date: Wed, 15 May 2024 17:24:44 +0530 Subject: [PATCH] feat: add support for HTTP and SOCKS5 proxy for telegram client --- README.md | 1 + cmd/run.go | 1 + go.mod | 1 + go.sum | 2 ++ internal/config/config.go | 1 + internal/tgc/tgc.go | 26 +++++++++++++++++++++----- internal/utils/proxy.go | 38 ++++++++++++++++++++++++++++++++++++++ pkg/services/auth.go | 2 +- 8 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 internal/utils/proxy.go diff --git a/README.md b/README.md index 7d4678e..470cb04 100644 --- a/README.md +++ b/README.md @@ -190,6 +190,7 @@ teldrive run --help | --tg-bg-bots-limit | Start at most this no of bots in the background to prevent connection recreation on every request.Increase this if you are streaming or downloading large no of files simultaneously. | No | 5 | --tg-uploads-threads | Concurrent Uploads threads for uploading file | No | 16 | | --tg-uploads-retention | Uploads retention duration.Duration to keep failed uploaded chunks in db for resuming uploads. | No | 7d | +| --tg-proxy | Socks5 or HTTP proxy for telegram client. | No | "" | **You Can also set config values through env varibles.** - For example ```tg-session-file``` will become ```TELDRIVE_TG_SESSION_FILE``` same for all possible flags. diff --git a/cmd/run.go b/cmd/run.go index ffb996b..474a6ae 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -80,6 +80,7 @@ func NewRun() *cobra.Command { runCmd.Flags().StringVar(&config.TG.LangCode, "tg-lang-code", "en", "Language code") runCmd.Flags().StringVar(&config.TG.SystemLangCode, "tg-system-lang-code", "en-US", "System language code") runCmd.Flags().StringVar(&config.TG.LangPack, "tg-lang-pack", "webk", "Language pack") + runCmd.Flags().StringVar(&config.TG.Proxy, "tg-proxy", "", "HTTP OR SOCKS5 proxy URL") runCmd.Flags().IntVar(&config.TG.BgBotsLimit, "tg-bg-bots-limit", 5, "Background bots limit") runCmd.Flags().BoolVar(&config.TG.DisableStreamBots, "tg-disable-stream-bots", false, "Disable stream bots") runCmd.Flags().StringVar(&config.TG.Uploads.EncryptionKey, "tg-uploads-encryption-key", "", "Uploads encryption key") diff --git a/go.mod b/go.mod index 13cad6d..b2162a5 100644 --- a/go.mod +++ b/go.mod @@ -39,6 +39,7 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/iyear/connectproxy v0.1.1 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/mfridman/interpolate v0.0.2 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect diff --git a/go.sum b/go.sum index d993d8b..2da9ada 100644 --- a/go.sum +++ b/go.sum @@ -91,6 +91,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/iyear/connectproxy v0.1.1 h1:JZOF/62vvwRGBWcgSyWRb0BpKD4FSs0++B5/y5pNE4c= +github.com/iyear/connectproxy v0.1.1/go.mod h1:yD4zOmSMQCmwHIT4fk8mg4k2M15z8VoMSoeY6NNJdsA= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= diff --git a/internal/config/config.go b/internal/config/config.go index d42d5df..b6df1a9 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -32,6 +32,7 @@ type TGConfig struct { SessionFile string BgBotsLimit int DisableStreamBots bool + Proxy string Uploads struct { EncryptionKey string Threads int diff --git a/internal/tgc/tgc.go b/internal/tgc/tgc.go index 9102e67..9e63089 100644 --- a/internal/tgc/tgc.go +++ b/internal/tgc/tgc.go @@ -9,11 +9,15 @@ import ( "github.com/divyam234/teldrive/internal/kv" "github.com/divyam234/teldrive/internal/recovery" "github.com/divyam234/teldrive/internal/retry" + "github.com/divyam234/teldrive/internal/utils" "github.com/gotd/contrib/middleware/floodwait" "github.com/gotd/contrib/middleware/ratelimit" tdclock "github.com/gotd/td/clock" "github.com/gotd/td/session" "github.com/gotd/td/telegram" + "github.com/gotd/td/telegram/dcs" + "github.com/pkg/errors" + "golang.org/x/net/proxy" "golang.org/x/time/rate" ) @@ -26,9 +30,21 @@ func defaultMiddlewares(ctx context.Context) ([]telegram.Middleware, error) { }, nil } -func New(ctx context.Context, config *config.TGConfig, handler telegram.UpdateHandler, storage session.Storage, middlewares ...telegram.Middleware) *telegram.Client { +func New(ctx context.Context, config *config.TGConfig, handler telegram.UpdateHandler, storage session.Storage, middlewares ...telegram.Middleware) (*telegram.Client, error) { + + var dialer dcs.DialFunc = proxy.Direct.DialContext + if config.Proxy != "" { + d, err := utils.Proxy.GetDial(config.Proxy) + if err != nil { + return nil, errors.Wrap(err, "get dialer") + } + dialer = d.DialContext + } opts := telegram.Options{ + Resolver: dcs.Plain(dcs.PlainOptions{ + Dial: dialer, + }), Device: telegram.DeviceConfig{ DeviceModel: config.DeviceModel, SystemVersion: config.SystemVersion, @@ -45,10 +61,10 @@ func New(ctx context.Context, config *config.TGConfig, handler telegram.UpdateHa UpdateHandler: handler, } - return telegram.NewClient(config.AppId, config.AppHash, opts) + return telegram.NewClient(config.AppId, config.AppHash, opts), nil } -func NoAuthClient(ctx context.Context, config *config.TGConfig, handler telegram.UpdateHandler, storage session.Storage) *telegram.Client { +func NoAuthClient(ctx context.Context, config *config.TGConfig, handler telegram.UpdateHandler, storage session.Storage) (*telegram.Client, error) { middlewares, _ := defaultMiddlewares(ctx) middlewares = append(middlewares, ratelimit.New(rate.Every(time.Millisecond*100), 5)) return New(ctx, config, handler, storage, middlewares...) @@ -72,7 +88,7 @@ func AuthClient(ctx context.Context, config *config.TGConfig, sessionStr string) middlewares, _ := defaultMiddlewares(ctx) middlewares = append(middlewares, ratelimit.New(rate.Every(time.Millisecond* time.Duration(config.Rate)), config.RateBurst)) - return New(ctx, config, nil, storage, middlewares...), nil + return New(ctx, config, nil, storage, middlewares...) } func BotClient(ctx context.Context, KV kv.KV, config *config.TGConfig, token string) (*telegram.Client, error) { @@ -83,7 +99,7 @@ func BotClient(ctx context.Context, KV kv.KV, config *config.TGConfig, token str time.Duration(config.Rate)), config.RateBurst)) } - return New(ctx, config, nil, storage, middlewares...), nil + return New(ctx, config, nil, storage, middlewares...) } func Backoff(_clock tdclock.Clock) backoff.BackOff { b := backoff.NewExponentialBackOff() diff --git a/internal/utils/proxy.go b/internal/utils/proxy.go new file mode 100644 index 0000000..76ab26d --- /dev/null +++ b/internal/utils/proxy.go @@ -0,0 +1,38 @@ +// Taken from /iyear/tdl + +package utils + +import ( + "net/url" + + "github.com/go-faster/errors" + "github.com/iyear/connectproxy" + "golang.org/x/net/proxy" +) + +type _proxy struct{} + +var Proxy = _proxy{} + +func init() { + connectproxy.Register(&connectproxy.Config{ + InsecureSkipVerify: true, + }) +} + +func (p _proxy) GetDial(_url string) (proxy.ContextDialer, error) { + u, err := url.Parse(_url) + if err != nil { + return nil, errors.Wrap(err, "parse proxy url") + } + dialer, err := proxy.FromURL(u, proxy.Direct) + if err != nil { + return nil, errors.Wrap(err, "proxy from url") + } + + if d, ok := dialer.(proxy.ContextDialer); ok { + return d, nil + } + + return nil, errors.New("proxy dialer is not ContextDialer") +} diff --git a/pkg/services/auth.go b/pkg/services/auth.go index ae5e406..d7ff3cf 100644 --- a/pkg/services/auth.go +++ b/pkg/services/auth.go @@ -184,7 +184,7 @@ func (as *AuthService) HandleMultipleLogin(c *gin.Context) { dispatcher := tg.NewUpdateDispatcher() loggedIn := qrlogin.OnLoginToken(dispatcher) sessionStorage := &session.StorageMemory{} - tgClient := tgc.NoAuthClient(c, &as.cnf.TG, dispatcher, sessionStorage) + tgClient, _ := tgc.NoAuthClient(c, &as.cnf.TG, dispatcher, sessionStorage) err = tgClient.Run(c, func(ctx context.Context) error { for {