client: add initial proxy support (#1062)

This commit is contained in:
lavacat
2026-04-16 13:14:29 +03:00
committed by GitHub
parent 0f0b21b22c
commit d7864fcd3a
7 changed files with 113 additions and 4 deletions

View File

@@ -210,11 +210,15 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge
Storage: client.ScopedStore,
AccessHasher: client.ScopedStore,
})
resolver, err := GetProxyResolver(tc.Config.ProxyConfig)
if err != nil {
return nil, err
}
client.client = telegram.NewClient(tc.Config.APIID, tc.Config.APIHash, telegram.Options{
CustomSessionStorage: &login.Metadata.(*UserLoginMetadata).Session,
Logger: zaplog,
UpdateHandler: client.updatesManager,
Resolver: resolver,
OnDead: client.onDead,
OnSession: client.onSession,
OnConnected: client.onConnected,

View File

@@ -55,6 +55,13 @@ type DeviceInfo struct {
LangCode string `yaml:"lang_code"`
}
type ProxyConfig struct {
Type string `yaml:"type"`
Address string `yaml:"address"`
Username string `yaml:"username"`
Password string `yaml:"password"`
}
type TelegramConfig struct {
APIID int `yaml:"api_id"`
APIHash string `yaml:"api_hash"`
@@ -68,6 +75,8 @@ type TelegramConfig struct {
TimeoutSeconds int `yaml:"timeout_seconds"`
} `yaml:"ping"`
ProxyConfig ProxyConfig `yaml:"proxy"`
Sync struct {
UpdateLimit int `yaml:"update_limit"`
CreateLimit int `yaml:"create_limit"`
@@ -161,6 +170,10 @@ func upgradeConfig(helper up.Helper) {
helper.Copy(up.Bool, "member_list", "skip_deleted")
helper.Copy(up.Int, "ping", "interval_seconds")
helper.Copy(up.Int, "ping", "timeout_seconds")
helper.Copy(up.Str, "proxy", "type")
helper.Copy(up.Str|up.Null, "proxy", "address")
helper.Copy(up.Str|up.Null, "proxy", "username")
helper.Copy(up.Str|up.Null, "proxy", "password")
helper.Copy(up.Int, "sync", "update_limit")
helper.Copy(up.Int, "sync", "create_limit")
helper.Copy(up.Int, "sync", "login_sync_limit")
@@ -187,6 +200,7 @@ func (tc *TelegramConnector) GetConfig() (example string, data any, upgrader up.
{"animated_sticker"},
{"member_list"},
{"ping"},
{"proxy"},
{"sync"},
{"takeout"},
{"max_member_count"},

View File

@@ -54,6 +54,16 @@ ping:
# The timeout (in seconds) for a single ping.
timeout_seconds: 10
# Proxy settings
proxy:
# Allowed types: disabled, socks5, mtproxy
type: disabled
# Proxy IP address/domain name and port.
address: "127.0.0.1:1080"
# Proxy authentication (optional). Put MTProxy secret in password field.
username:
password:
sync:
# Number of most recently active dialogs to check when syncing chats.
# Set to -1 to remove limit.

View File

@@ -125,7 +125,12 @@ func (bl *baseLogin) makeClient(ctx context.Context, dispatcher *tg.UpdateDispat
if dispatcher == nil {
dispatcher = ptr.Ptr(tg.NewUpdateDispatcher())
}
resolver, err := GetProxyResolver(bl.main.Config.ProxyConfig)
if err != nil {
return err
}
bl.client = telegram.NewClient(bl.main.Config.APIID, bl.main.Config.APIHash, telegram.Options{
Resolver: resolver,
CustomSessionStorage: &bl.session,
Logger: zaplog,
Device: bl.main.deviceConfig(),

View File

@@ -63,7 +63,16 @@ func (bl *BotLogin) SubmitUserInput(ctx context.Context, input map[string]string
ctx = log.WithContext(ctx)
botToken := input[LoginStepIDBotToken]
err := logoutBotAPI(ctx, botToken)
dialFunc, err := GetProxyDialFunc(bl.main.Config.ProxyConfig)
if err != nil {
return nil, err
}
httpClient := &http.Client{
Transport: &http.Transport{
DialContext: dialFunc,
},
}
err = logoutBotAPI(ctx, botToken, httpClient)
if err != nil {
return nil, fmt.Errorf("failed to logout from bot API: %w", err)
}
@@ -86,12 +95,12 @@ type botAPIResponse struct {
Description string `json:"description"`
}
func logoutBotAPI(ctx context.Context, token string) error {
func logoutBotAPI(ctx context.Context, token string, client *http.Client) error {
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://api.telegram.org/bot"+token+"/logOut", nil)
if err != nil {
return fmt.Errorf("failed to prepare request: %w", err)
}
resp, err := http.DefaultClient.Do(req)
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to send request: %w", err)
}

61
pkg/connector/proxy.go Normal file
View File

@@ -0,0 +1,61 @@
// mautrix-telegram - A Matrix-Telegram puppeting bridge.
// Copyright (C) 2026 Vladislav Agarkov
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package connector
import (
"fmt"
"golang.org/x/net/proxy"
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/dcs"
)
func GetProxyDialFunc(cfg ProxyConfig) (dcs.DialFunc, error) {
switch cfg.Type {
// we can't proxy HTTP through mtproxy
case "disabled", "mtproxy":
return nil, nil
case "socks5":
var auth *proxy.Auth
if cfg.Username != "" && cfg.Password != "" {
auth = &proxy.Auth{User: cfg.Username, Password: cfg.Password}
}
sock5, err := proxy.SOCKS5("tcp", cfg.Address, auth, proxy.Direct)
if err != nil {
return nil, err
}
return sock5.(proxy.ContextDialer).DialContext, nil
default:
return nil, fmt.Errorf("unsupported proxy type %s", cfg.Type)
}
}
func GetProxyResolver(cfg ProxyConfig) (dcs.Resolver, error) {
switch cfg.Type {
case "disabled", "socks5":
dialer, err := GetProxyDialFunc(cfg)
if err != nil {
return nil, err
}
resolver := dcs.Plain(dcs.PlainOptions{Dial: dialer})
return resolver, nil
case "mtproxy":
return dcs.MTProxy(cfg.Address, []byte(cfg.Password), dcs.MTProxyOptions{})
default:
return nil, fmt.Errorf("unsupported proxy type %s", cfg.Type)
}
}