client: add initial proxy support (#1062)
This commit is contained in:
@@ -88,6 +88,12 @@ func migrateLegacyConfig(helper up.Helper) {
|
|||||||
bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "max_initial_member_sync"}, []string{"network", "member_list", "max_initial_sync"})
|
bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "max_initial_member_sync"}, []string{"network", "member_list", "max_initial_sync"})
|
||||||
bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "sync_channel_members"}, []string{"network", "member_list", "sync_broadcast_channels"})
|
bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "sync_channel_members"}, []string{"network", "member_list", "sync_broadcast_channels"})
|
||||||
bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "skip_deleted_members"}, []string{"network", "member_list", "skip_deleted"})
|
bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "skip_deleted_members"}, []string{"network", "member_list", "skip_deleted"})
|
||||||
|
bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"telegram", "proxy", "type"}, []string{"network", "proxy", "type"})
|
||||||
|
proxyAddress, _ := helper.Get(up.Str, "telegram", "proxy", "address")
|
||||||
|
proxyPort, _ := helper.Get(up.Int, "telegram", "proxy", "port")
|
||||||
|
helper.Set(up.Str, fmt.Sprintf("%s:%s", proxyAddress, proxyPort), "network", "proxy", "address")
|
||||||
|
bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"telegram", "proxy", "username"}, []string{"network", "proxy", "username"})
|
||||||
|
bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"telegram", "proxy", "password"}, []string{"network", "proxy", "password"})
|
||||||
bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "max_member_count"}, []string{"network", "max_member_count"})
|
bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "max_member_count"}, []string{"network", "max_member_count"})
|
||||||
bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "sync_update_limit"}, []string{"network", "sync", "update_limit"})
|
bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "sync_update_limit"}, []string{"network", "sync", "update_limit"})
|
||||||
bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "sync_create_limit"}, []string{"network", "sync", "create_limit"})
|
bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "sync_create_limit"}, []string{"network", "sync", "create_limit"})
|
||||||
|
|||||||
@@ -210,11 +210,15 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge
|
|||||||
Storage: client.ScopedStore,
|
Storage: client.ScopedStore,
|
||||||
AccessHasher: 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{
|
client.client = telegram.NewClient(tc.Config.APIID, tc.Config.APIHash, telegram.Options{
|
||||||
CustomSessionStorage: &login.Metadata.(*UserLoginMetadata).Session,
|
CustomSessionStorage: &login.Metadata.(*UserLoginMetadata).Session,
|
||||||
Logger: zaplog,
|
Logger: zaplog,
|
||||||
UpdateHandler: client.updatesManager,
|
UpdateHandler: client.updatesManager,
|
||||||
|
Resolver: resolver,
|
||||||
OnDead: client.onDead,
|
OnDead: client.onDead,
|
||||||
OnSession: client.onSession,
|
OnSession: client.onSession,
|
||||||
OnConnected: client.onConnected,
|
OnConnected: client.onConnected,
|
||||||
|
|||||||
@@ -55,6 +55,13 @@ type DeviceInfo struct {
|
|||||||
LangCode string `yaml:"lang_code"`
|
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 {
|
type TelegramConfig struct {
|
||||||
APIID int `yaml:"api_id"`
|
APIID int `yaml:"api_id"`
|
||||||
APIHash string `yaml:"api_hash"`
|
APIHash string `yaml:"api_hash"`
|
||||||
@@ -68,6 +75,8 @@ type TelegramConfig struct {
|
|||||||
TimeoutSeconds int `yaml:"timeout_seconds"`
|
TimeoutSeconds int `yaml:"timeout_seconds"`
|
||||||
} `yaml:"ping"`
|
} `yaml:"ping"`
|
||||||
|
|
||||||
|
ProxyConfig ProxyConfig `yaml:"proxy"`
|
||||||
|
|
||||||
Sync struct {
|
Sync struct {
|
||||||
UpdateLimit int `yaml:"update_limit"`
|
UpdateLimit int `yaml:"update_limit"`
|
||||||
CreateLimit int `yaml:"create_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.Bool, "member_list", "skip_deleted")
|
||||||
helper.Copy(up.Int, "ping", "interval_seconds")
|
helper.Copy(up.Int, "ping", "interval_seconds")
|
||||||
helper.Copy(up.Int, "ping", "timeout_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", "update_limit")
|
||||||
helper.Copy(up.Int, "sync", "create_limit")
|
helper.Copy(up.Int, "sync", "create_limit")
|
||||||
helper.Copy(up.Int, "sync", "login_sync_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"},
|
{"animated_sticker"},
|
||||||
{"member_list"},
|
{"member_list"},
|
||||||
{"ping"},
|
{"ping"},
|
||||||
|
{"proxy"},
|
||||||
{"sync"},
|
{"sync"},
|
||||||
{"takeout"},
|
{"takeout"},
|
||||||
{"max_member_count"},
|
{"max_member_count"},
|
||||||
|
|||||||
@@ -54,6 +54,16 @@ ping:
|
|||||||
# The timeout (in seconds) for a single ping.
|
# The timeout (in seconds) for a single ping.
|
||||||
timeout_seconds: 10
|
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:
|
sync:
|
||||||
# Number of most recently active dialogs to check when syncing chats.
|
# Number of most recently active dialogs to check when syncing chats.
|
||||||
# Set to -1 to remove limit.
|
# Set to -1 to remove limit.
|
||||||
|
|||||||
@@ -125,7 +125,12 @@ func (bl *baseLogin) makeClient(ctx context.Context, dispatcher *tg.UpdateDispat
|
|||||||
if dispatcher == nil {
|
if dispatcher == nil {
|
||||||
dispatcher = ptr.Ptr(tg.NewUpdateDispatcher())
|
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{
|
bl.client = telegram.NewClient(bl.main.Config.APIID, bl.main.Config.APIHash, telegram.Options{
|
||||||
|
Resolver: resolver,
|
||||||
CustomSessionStorage: &bl.session,
|
CustomSessionStorage: &bl.session,
|
||||||
Logger: zaplog,
|
Logger: zaplog,
|
||||||
Device: bl.main.deviceConfig(),
|
Device: bl.main.deviceConfig(),
|
||||||
|
|||||||
@@ -63,7 +63,16 @@ func (bl *BotLogin) SubmitUserInput(ctx context.Context, input map[string]string
|
|||||||
ctx = log.WithContext(ctx)
|
ctx = log.WithContext(ctx)
|
||||||
|
|
||||||
botToken := input[LoginStepIDBotToken]
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to logout from bot API: %w", err)
|
return nil, fmt.Errorf("failed to logout from bot API: %w", err)
|
||||||
}
|
}
|
||||||
@@ -86,12 +95,12 @@ type botAPIResponse struct {
|
|||||||
Description string `json:"description"`
|
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)
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://api.telegram.org/bot"+token+"/logOut", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to prepare request: %w", err)
|
return fmt.Errorf("failed to prepare request: %w", err)
|
||||||
}
|
}
|
||||||
resp, err := http.DefaultClient.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to send request: %w", err)
|
return fmt.Errorf("failed to send request: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user