package main import ( "log" "os" "strconv" "strings" "time" ) type Config struct { Token string AppID string GuildID string PublicOrderChannelID string InternalOrderChannelID string AuditChannelID string AdminRoleIDs []string TeamRoleIDs []string TradingRoleIDs []string TradingReportChannelID string TradingShips []string TradingCommodities []string TradingOrgSharePercent map[string]float64 DatabasePath string ThreadDeleteAfter time.Duration DashboardAddr string DashboardToken string TradingPayoutRecipient string } func loadConfig() Config { cfg := Config{ Token: os.Getenv("DISCORD_TOKEN"), AppID: os.Getenv("DISCORD_APP_ID"), GuildID: os.Getenv("DISCORD_GUILD_ID"), PublicOrderChannelID: getenv("DISCORD_PUBLIC_ORDER_CHANNEL_ID", os.Getenv("DISCORD_ORDER_CHANNEL_ID")), InternalOrderChannelID: getenv("DISCORD_INTERNAL_ORDER_CHANNEL_ID", getenv("DISCORD_ORDER_CHANNEL_ID", "")), AuditChannelID: os.Getenv("DISCORD_AUDIT_CHANNEL_ID"), AdminRoleIDs: splitCSV(os.Getenv("DISCORD_ADMIN_ROLE_IDS")), TeamRoleIDs: splitCSV(os.Getenv("DISCORD_TEAM_ROLE_IDS")), TradingRoleIDs: splitCSV(os.Getenv("DISCORD_TRADING_ROLE_IDS")), TradingReportChannelID: getenv("DISCORD_TRADING_REPORT_CHANNEL_ID", os.Getenv("DISCORD_AUDIT_CHANNEL_ID")), TradingShips: splitCSV(os.Getenv("TRADING_SHIPS")), TradingCommodities: splitCSV(os.Getenv("TRADING_COMMODITIES")), TradingOrgSharePercent: tradingShareConfig(), DatabasePath: getenv("DATABASE_PATH", "orders.db"), ThreadDeleteAfter: durationHoursEnv("THREAD_DELETE_AFTER_HOURS", 72), DashboardAddr: getenv("DASHBOARD_ADDR", ""), DashboardToken: os.Getenv("DASHBOARD_TOKEN"), TradingPayoutRecipient: getenv("TRADING_PAYOUT_RECIPIENT", "Orga-Kasse"), } if cfg.Token == "" || cfg.AppID == "" || cfg.InternalOrderChannelID == "" { log.Fatal("Bitte DISCORD_TOKEN, DISCORD_APP_ID und DISCORD_INTERNAL_ORDER_CHANNEL_ID setzen") } if len(cfg.AdminRoleIDs) == 0 { log.Println("WARNUNG: DISCORD_ADMIN_ROLE_IDS ist leer. Admin-Kommandos sind nur fuer Server-Administratoren verfuegbar.") } return cfg } func getenv(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback } func splitCSV(v string) []string { var out []string for _, p := range strings.Split(v, ",") { p = strings.TrimSpace(p) if p != "" { out = append(out, p) } } return out } func durationHoursEnv(key string, fallbackHours int) time.Duration { v := strings.TrimSpace(os.Getenv(key)) if v == "" { return time.Duration(fallbackHours) * time.Hour } hours, err := strconv.Atoi(v) if err != nil || hours < 0 { log.Printf("WARNUNG: %s ist ungültig, verwende %d Stunden", key, fallbackHours) return time.Duration(fallbackHours) * time.Hour } return time.Duration(hours) * time.Hour } func floatEnv(key string, fallback float64) float64 { v := strings.TrimSpace(os.Getenv(key)) if v == "" { return fallback } parsed, err := strconv.ParseFloat(strings.ReplaceAll(v, ",", "."), 64) if err != nil { log.Printf("WARNUNG: %s ist ungültig, verwende %.2f", key, fallback) return fallback } return parsed } func tradingShareConfig() map[string]float64 { return map[string]float64{ TradingPurposeTrade: normalizePercent(floatEnv("TRADING_SHARE_TRADE", 0)), TradingPurposeHauling: normalizePercent(floatEnv("TRADING_SHARE_HAULING", 0)), TradingPurposeEvent: normalizePercent(floatEnv("TRADING_SHARE_EVENT", 0)), TradingPurposeIntern: normalizePercent(floatEnv("TRADING_SHARE_INTERN", 0)), } } func normalizePercent(v float64) float64 { // Erlaubt sowohl "10" als auch "0.10" in der .env. if v > 1 { return v / 100 } if v < 0 { return 0 } return v }