package config import ( "os" "strconv" "strings" "time" ) // Account describes a Fio bank account. type Account struct { IBAN string // e.g. "CZ0820100000002502035405" AcctNum string // bare account number, e.g. "2502035405" TokenEnv string // env var name holding the optional Fio API token Primary bool // true for the account QR codes default to } // LoadedAccount is an Account with its token resolved from the environment. type LoadedAccount struct { Account Token string // value of os.Getenv(Account.TokenEnv); empty → transparent-scraper path } // Accounts is the hardcoded list of Fio bank accounts to sync from. // The first entry with Primary=true is used for QR codes. // Tokens are loaded at runtime from each account's TokenEnv. var Accounts = []Account{ {IBAN: "CZ0820100000002502035405", AcctNum: "2502035405", TokenEnv: "FIO_API_TOKEN_NEW", Primary: true}, {IBAN: "CZ8520100000002800359168", AcctNum: "2800359168", TokenEnv: "FIO_API_TOKEN_OLD"}, } // Google Sheets IDs — change in code if sheets change (not from env). const ( AttendanceSheetID = "1E2e_gT_K5AwSRCDLDTa2UetZTkHmBOcz0kFbBUNUNBA" PaymentsSheetID = "1Om0YPoDVCH5cV8BrNz5LG5eR5MMU05ypQC7UMN1xn_Y" // Both attendance tabs live in the same Google Spreadsheet (AttendanceSheetID). // The original adult and junior attendance data lives in separate source spreadsheets, // but is collected into this one sheet via IMPORTRANGE — one tab per group. // Tabs are identified by the gid= query param in the CSV export URL. AttendanceAdultSheetGID = "0" // gid=0 — adult practices tab (IMPORTRANGE'd) JuniorSheetGID = "1213318614" // gid=1213318614 — junior practices tab (IMPORTRANGE'd) ) // CacheSheetMap mirrors scripts/config.py CACHE_SHEET_MAP. // Maps a cache key to the Google Sheet ID whose Drive modifiedTime gates it. // Both attendance keys map to the same spreadsheet — different tabs, one Drive file. var CacheSheetMap = map[string]string{ "attendance_regular": AttendanceSheetID, "attendance_juniors": AttendanceSheetID, "exceptions_dict": PaymentsSheetID, "payments_transactions": PaymentsSheetID, } // Config holds all runtime configuration loaded from environment variables. // Mirrors scripts/config.py. type Config struct { CredentialsPath string QRAccount string // IBAN of the primary account used for QR codes LoadedAccounts []LoadedAccount // all accounts to sync, tokens resolved from env CacheDir string CacheTTL time.Duration CacheAPICheckTTL time.Duration DriveTimeout time.Duration LogLevel string ServerAddr string } // Load reads configuration from the environment, applying defaults that // match the Python side. func Load() Config { loaded := make([]LoadedAccount, len(Accounts)) var qrAccount string for i, a := range Accounts { loaded[i] = LoadedAccount{Account: a, Token: os.Getenv(a.TokenEnv)} if a.Primary { qrAccount = a.IBAN } } return Config{ CredentialsPath: env("CREDENTIALS_PATH", ".secret/fuj-management-bot-credentials.json"), QRAccount: qrAccount, LoadedAccounts: loaded, CacheDir: env("CACHE_DIR", "tmp/go"), CacheTTL: envDuration("CACHE_TTL_SECONDS", 300), CacheAPICheckTTL: envDuration("CACHE_API_CHECK_TTL_SECONDS", 300), DriveTimeout: envDuration("DRIVE_TIMEOUT_SECONDS", 10), LogLevel: env("LOG_LEVEL", "INFO"), ServerAddr: env("SERVER_ADDR", ":8080"), } } // IBANAccountNum extracts the bare account number from a Czech IBAN. // "CZ8520100000002800359168" → "2800359168" // Structure: CZ(2 check)(4 bank code)(16 zero-padded account). func IBANAccountNum(iban string) string { s := strings.ReplaceAll(iban, " ", "") if len(s) < 8 { return iban } raw := s[8:] // 16-digit zero-padded account portion n := strings.TrimLeft(raw, "0") if n == "" { return "0" } return n } func env(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback } func envDuration(key string, defaultSeconds int) time.Duration { if v := os.Getenv(key); v != "" { if n, err := strconv.Atoi(v); err == nil && n > 0 { return time.Duration(n) * time.Second } } return time.Duration(defaultSeconds) * time.Second }