// Package config loads the Telegram connector's environment configuration. package config import ( "fmt" "os" "strconv" "strings" pkgtel "scrabble/pkg/telemetry" ) // Languages is the set of service languages a bot may be tagged with. Each is a // separate bot (own token + game channel) serving that audience; the same Telegram // user id spans them all (ARCHITECTURE.md §12). var Languages = []string{"en", "ru"} // BotConfig is one language-tagged bot's settings. type BotConfig struct { // Token is the Telegram Bot API token (TELEGRAM_BOT_TOKEN_). It both // authenticates the Bot API client and is the HMAC secret for Mini App initData // validation against this bot. Token string // GameChannelID is the chat id of this bot's game channel for SendToGameChannel // (TELEGRAM_GAME_CHANNEL_ID_, optional; 0 disables channel posts). GameChannelID int64 } // Config is the Telegram connector's runtime configuration, read from the // environment. The bot tokens live only in this process (ARCHITECTURE.md §12). type Config struct { // Bots maps a service language (one of Languages) to that language's bot // settings. A language is present when its TELEGRAM_BOT_TOKEN_ is set; at // least one bot is required. Bots map[string]BotConfig // GRPCAddr is the listen address of the connector gRPC server that gateway and // backend call (TELEGRAM_GRPC_ADDR, default :9091). GRPCAddr string // MiniAppURL is the HTTPS origin of the Mini App registered with BotFather; it // is the base of every launch button, to which a deep-link adds a startapp // query parameter (TELEGRAM_MINIAPP_URL, required). It is shared by all bots // (one gateway origin); initData is signed per bot token. MiniAppURL string // APIBaseURL overrides the Bot API host (TELEGRAM_API_BASE_URL, optional; // default https://api.telegram.org) — used for a local mock or a self-hosted // Bot API server. Shared by all bots. APIBaseURL string // TestEnv routes the Bot API client to Telegram's test environment // (.../bot/test/METHOD) (TELEGRAM_TEST_ENV=true, default false). TestEnv bool // LogLevel is the zap log level (TELEGRAM_LOG_LEVEL, default info). LogLevel string // Telemetry configures the OpenTelemetry providers (shared bootstrap). Telemetry pkgtel.Config } // Load reads the connector configuration from the environment, applying defaults // and validating the required fields. func Load() (Config, error) { cfg := Config{ Bots: map[string]BotConfig{}, GRPCAddr: envOr("TELEGRAM_GRPC_ADDR", ":9091"), MiniAppURL: os.Getenv("TELEGRAM_MINIAPP_URL"), APIBaseURL: os.Getenv("TELEGRAM_API_BASE_URL"), TestEnv: os.Getenv("TELEGRAM_TEST_ENV") == "true", LogLevel: envOr("TELEGRAM_LOG_LEVEL", "info"), } for _, lang := range Languages { suffix := strings.ToUpper(lang) token := os.Getenv("TELEGRAM_BOT_TOKEN_" + suffix) if token == "" { continue } bot := BotConfig{Token: token} if v := strings.TrimSpace(os.Getenv("TELEGRAM_GAME_CHANNEL_ID_" + suffix)); v != "" { id, err := strconv.ParseInt(v, 10, 64) if err != nil { return Config{}, fmt.Errorf("config: TELEGRAM_GAME_CHANNEL_ID_%s %q: %w", suffix, v, err) } bot.GameChannelID = id } cfg.Bots[lang] = bot } tel := pkgtel.DefaultConfig("scrabble-telegram") tel.ServiceName = envOr("TELEGRAM_SERVICE_NAME", tel.ServiceName) tel.TracesExporter = envOr("TELEGRAM_OTEL_TRACES_EXPORTER", tel.TracesExporter) tel.MetricsExporter = envOr("TELEGRAM_OTEL_METRICS_EXPORTER", tel.MetricsExporter) cfg.Telemetry = tel if len(cfg.Bots) == 0 { return Config{}, fmt.Errorf("config: at least one TELEGRAM_BOT_TOKEN_ (LANG in %v) is required", Languages) } if cfg.MiniAppURL == "" { return Config{}, fmt.Errorf("config: TELEGRAM_MINIAPP_URL is required") } if err := cfg.Telemetry.Validate(); err != nil { return Config{}, fmt.Errorf("config: %w", err) } return cfg, nil } // envOr returns the environment value for key, or def when it is unset or empty. func envOr(key, def string) string { if v := os.Getenv(key); v != "" { return v } return def }