// mautrix-telegram - A Matrix-Telegram puppeting bridge. // Copyright (C) 2025 Sumner Evans // // 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 . package connector import ( _ "embed" "fmt" "slices" up "go.mau.fi/util/configupgrade" "maunium.net/go/mautrix/bridgev2" "maunium.net/go/mautrix/bridgev2/bridgeconfig" "maunium.net/go/mautrix/id" "go.mau.fi/mautrix-telegram/pkg/connector/media" ) var _ bridgev2.ConfigValidatingNetwork = (*TelegramConnector)(nil) type MemberListConfig struct { MaxInitialSync int `yaml:"max_initial_sync"` SyncBroadcastChannels bool `yaml:"sync_broadcast_channels"` SkipDeleted bool `yaml:"skip_deleted"` } func (c MemberListConfig) NormalizedMaxInitialSync() int { if c.MaxInitialSync < 0 { return 10_000 } return c.MaxInitialSync } type DeviceInfo struct { DeviceModel string `yaml:"device_model"` SystemVersion string `yaml:"system_version"` AppVersion string `yaml:"app_version"` SystemLangCode string `yaml:"system_lang_code"` LangCode string `yaml:"lang_code"` } type TelegramConfig struct { APIID int `yaml:"api_id"` APIHash string `yaml:"api_hash"` DeviceInfo DeviceInfo `yaml:"device_info"` AnimatedSticker media.AnimatedStickerConfig `yaml:"animated_sticker"` MemberList MemberListConfig `yaml:"member_list"` Ping struct { IntervalSeconds int `yaml:"interval_seconds"` TimeoutSeconds int `yaml:"timeout_seconds"` } `yaml:"ping"` Sync struct { UpdateLimit int `yaml:"update_limit"` CreateLimit int `yaml:"create_limit"` LoginLimit int `yaml:"login_sync_limit"` DirectChats bool `yaml:"direct_chats"` } `yaml:"sync"` Takeout struct { DialogSync bool `yaml:"dialog_sync"` ForwardBackfill bool `yaml:"forward_backfill"` BackwardBackfill bool `yaml:"backward_backfill"` } `yaml:"takeout"` ContactAvatars bool `yaml:"contact_avatars"` ContactNames bool `yaml:"contact_names"` MaxMemberCount int `yaml:"max_member_count"` AlwaysCustomEmojiReaction bool `yaml:"always_custom_emoji_reaction"` SavedMessagesAvatar id.ContentURIString `yaml:"saved_message_avatar"` AlwaysTombstoneOnSupergroupMigration bool `yaml:"always_tombstone_on_supergroup_migration"` ImageAsFilePixels int `yaml:"image_as_file_pixels"` DisableViewOnce bool `yaml:"disable_view_once"` } func (c TelegramConfig) ShouldBridge(participantCount int) bool { return c.MaxMemberCount < 0 || participantCount <= c.MaxMemberCount } //go:embed example-config.yaml var ExampleConfig string func upgradeConfig(helper up.Helper) { bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"app_id"}, []string{"api_id"}) bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"app_hash"}, []string{"api_hash"}) helper.Copy(up.Int, "api_id") helper.Copy(up.Str, "api_hash") helper.Copy(up.Str|up.Null, "device_info", "device_model") helper.Copy(up.Str|up.Null, "device_info", "system_version") helper.Copy(up.Str|up.Null, "device_info", "app_version") helper.Copy(up.Str|up.Null, "device_info", "system_lang_code") helper.Copy(up.Str|up.Null, "device_info", "lang_code") helper.Copy(up.Str, "animated_sticker", "target") helper.Copy(up.Bool, "animated_sticker", "convert_from_webm") helper.Copy(up.Int, "animated_sticker", "args", "width") helper.Copy(up.Int, "animated_sticker", "args", "height") helper.Copy(up.Int, "animated_sticker", "args", "fps") helper.Copy(up.Int, "member_list", "max_initial_sync") helper.Copy(up.Bool, "member_list", "sync_broadcast_channels") 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.Int, "sync", "update_limit") helper.Copy(up.Int, "sync", "create_limit") helper.Copy(up.Int, "sync", "login_sync_limit") helper.Copy(up.Bool, "sync", "direct_chats") helper.Copy(up.Bool, "takeout", "dialog_sync") helper.Copy(up.Bool, "takeout", "forward_backfill") helper.Copy(up.Bool, "takeout", "backward_backfill") helper.Copy(up.Bool, "contact_avatars") helper.Copy(up.Bool, "contact_names") helper.Copy(up.Int, "max_member_count") helper.Copy(up.Bool, "always_custom_emoji_reaction") helper.Copy(up.Str, "saved_message_avatar") helper.Copy(up.Bool, "always_tombstone_on_supergroup_migration") helper.Copy(up.Int, "image_as_file_pixels") helper.Copy(up.Bool, "disable_view_once") } func (tg *TelegramConnector) GetConfig() (example string, data any, upgrader up.Upgrader) { return ExampleConfig, &tg.Config, &up.StructUpgrader{ SimpleUpgrader: up.SimpleUpgrader(upgradeConfig), Blocks: [][]string{ {"device_info"}, {"animated_sticker"}, {"member_list"}, {"ping"}, {"sync"}, {"takeout"}, {"max_member_count"}, }, Base: ExampleConfig, } } func (tg *TelegramConnector) ValidateConfig() error { if tg.Config.APIID == 0 { return fmt.Errorf("api_id is required") } if tg.Config.APIHash == "" || tg.Config.APIHash == "tjyd5yge35lbodk1xwzw2jstp90k55qz" { return fmt.Errorf("api_hash is required") } if !slices.Contains([]string{"disable", "gif", "png", "webp", "webm"}, tg.Config.AnimatedSticker.Target) { return fmt.Errorf("unsupported animated sticker target: %s", tg.Config.AnimatedSticker.Target) } return nil }