| import 'dotenv/config'; |
| export function getConfig() { |
| const token = process.env.DISCORD_TOKEN?.trim(); |
| const guildId = process.env.GUILD_ID?.trim() || null; |
| const databaseUrl = process.env.DATABASE_URL?.trim(); |
| const port = Number(process.env.PORT || 7860); |
| const adminRoleName = process.env.ADMIN_ROLE_NAME?.trim() || 'Admin'; |
| const oddsApiKey = process.env.ODDS_API_KEY?.trim() || null; |
| const oddsApiBaseUrl = process.env.ODDS_API_BASE_URL?.trim() || 'https://api.the-odds-api.com/v4'; |
| const oddsApiSportKey = process.env.ODDS_API_SPORT_KEY?.trim() || 'baseball_mlb'; |
| const oddsApiRegions = process.env.ODDS_API_REGIONS?.trim() || 'us,us2,us_dfs,us_ex'; |
| const oddsApiBookmakers = (process.env.ODDS_API_BOOKMAKERS?.trim() |
| || 'fanduel,draftkings,betmgm,williamhill_us,fanatics,ballybet,hardrockbet,betr_us_dfs,pick6,prizepicks,underdog,kalshi,novig,polymarket,prophetx') |
| .split(',') |
| .map((value) => value.trim()) |
| .filter(Boolean); |
| const oddsApiMarkets = (process.env.ODDS_API_MARKETS?.trim() |
| || 'batter_home_runs,batter_hits,batter_total_bases,batter_rbis,batter_runs_scored,batter_hits_runs_rbis,pitcher_strikeouts') |
| .split(',') |
| .map((value) => value.trim()) |
| .filter(Boolean); |
| const circaDropboxUrl = process.env.CIRCA_DROPBOX_URL?.trim() || null; |
| const scanReportChannelId = process.env.SCAN_REPORT_CHANNEL_ID?.trim() || null; |
| const scanAlertChannelId = process.env.SCAN_ALERT_CHANNEL_ID?.trim() || null; |
| const scanMorningTime = process.env.SCAN_MORNING_TIME?.trim() || '08:00'; |
| const scanMorningFallbackTime = process.env.SCAN_MORNING_FALLBACK_TIME?.trim() || '09:45'; |
| const scanTimeZone = process.env.SCAN_TIMEZONE?.trim() || 'America/Chicago'; |
| const scanMinBooks = Number(process.env.SCAN_MIN_BOOKS || 6); |
| const scanDisagreementThreshold = Number(process.env.SCAN_DISAGREEMENT_THRESHOLD || 0.08); |
| const scanAlertCooldownMinutes = Number(process.env.SCAN_ALERT_COOLDOWN_MINUTES || 180); |
| const scanFrequencyMinutes = Number(process.env.SCAN_FREQUENCY_MINUTES || 15); |
| const scanHttpTimeoutMs = Number(process.env.SCAN_HTTP_TIMEOUT_MS || 15000); |
| const directOddsCacheTtlMs = Number(process.env.DIRECT_ODDS_CACHE_TTL_MS || 60000); |
| const sharpEdgeAlertThreshold = Number(process.env.SHARP_EDGE_ALERT_THRESHOLD || 0.04); |
| const sharpEdgeAlertMinBooks = Number(process.env.SHARP_EDGE_ALERT_MIN_BOOKS || 6); |
| const staleBookAlertThreshold = Number(process.env.STALE_BOOK_ALERT_THRESHOLD || 0.025); |
| const reverseAlertThreshold = Number(process.env.REVERSE_ALERT_THRESHOLD || 0.03); |
| const dfsScanMinBooks = Number(process.env.DFS_SCAN_MIN_BOOKS || 4); |
| const dfsEdgeAlertThreshold = Number(process.env.DFS_EDGE_ALERT_THRESHOLD || sharpEdgeAlertThreshold); |
| const dfsReverseAlertThreshold = Number(process.env.DFS_REVERSE_ALERT_THRESHOLD || reverseAlertThreshold); |
| const dfsStaleAlertThreshold = Number(process.env.DFS_STALE_ALERT_THRESHOLD || staleBookAlertThreshold); |
| const exchangeScanMinBooks = Number(process.env.EXCHANGE_SCAN_MIN_BOOKS || 4); |
| const exchangeEdgeAlertThreshold = Number(process.env.EXCHANGE_EDGE_ALERT_THRESHOLD || sharpEdgeAlertThreshold); |
| const exchangeReverseAlertThreshold = Number(process.env.EXCHANGE_REVERSE_ALERT_THRESHOLD || reverseAlertThreshold); |
| const exchangeStaleAlertThreshold = Number(process.env.EXCHANGE_STALE_ALERT_THRESHOLD || staleBookAlertThreshold); |
| const marketBoardDefaultLimit = Number(process.env.MARKET_BOARD_DEFAULT_LIMIT || 10); |
| const sharpEdgeAlertsEnabled = process.env.SHARP_EDGE_ALERTS_ENABLED?.trim() !== 'false'; |
| const staleBookAlertsEnabled = process.env.STALE_BOOK_ALERTS_ENABLED?.trim() !== 'false'; |
| const reverseAlertsEnabled = process.env.REVERSE_ALERTS_ENABLED?.trim() !== 'false'; |
| const circaChannelId = process.env.CIRCA_CHANNEL_ID?.trim() || null; |
| const circaDailyTime = process.env.CIRCA_DAILY_TIME?.trim() || '09:30'; |
| const circaTimeZone = process.env.CIRCA_TIMEZONE?.trim() || 'America/Chicago'; |
| const circaRetryMinutes = Number(process.env.CIRCA_RETRY_MINUTES || 30); |
| const circaMovementFrequencyMinutes = Number(process.env.CIRCA_MOVEMENT_FREQUENCY_MINUTES || 5); |
| const matchupHostedBaseUrl = process.env.MLB_HOSTED_BASE_URL?.trim() || null; |
| const matchupArtifactCacheTtlMs = Number(process.env.MATCHUP_ARTIFACT_CACHE_TTL_MS || 300000); |
| const matchupFallbackDays = Number(process.env.MATCHUP_FALLBACK_DAYS || 7); |
|
|
| if (!token) { |
| throw new Error('Missing DISCORD_TOKEN in environment.'); |
| } |
|
|
| if (!databaseUrl) { |
| throw new Error('Missing DATABASE_URL in environment.'); |
| } |
|
|
| return { |
| token, |
| guildId, |
| databaseUrl, |
| port, |
| adminRoleName, |
| matchups: { |
| enabled: Boolean(matchupHostedBaseUrl || databaseUrl), |
| hosted: { |
| baseUrl: matchupHostedBaseUrl, |
| cacheTtlMs: matchupArtifactCacheTtlMs, |
| fallbackDays: matchupFallbackDays, |
| }, |
| databaseUrl, |
| }, |
| scanner: { |
| enabled: Boolean( |
| circaDropboxUrl |
| && circaChannelId |
| || oddsApiKey |
| && scanReportChannelId |
| && scanAlertChannelId |
| && circaDropboxUrl |
| ), |
| oddsWorkflowEnabled: Boolean( |
| oddsApiKey |
| && scanReportChannelId |
| && scanAlertChannelId |
| && circaDropboxUrl |
| ), |
| circaWorkflowEnabled: Boolean(circaDropboxUrl && circaChannelId), |
| oddsApiKey, |
| oddsApiBaseUrl, |
| oddsApiSportKey, |
| oddsApiRegions, |
| oddsApiBookmakers, |
| oddsApiMarkets, |
| circaDropboxUrl, |
| scanReportChannelId, |
| scanAlertChannelId, |
| scanMorningTime, |
| scanMorningFallbackTime, |
| scanTimeZone, |
| scanMinBooks, |
| scanDisagreementThreshold, |
| scanAlertCooldownMinutes, |
| scanFrequencyMinutes, |
| scanHttpTimeoutMs, |
| directOddsCacheTtlMs, |
| sharpEdgeAlertThreshold, |
| sharpEdgeAlertMinBooks, |
| staleBookAlertThreshold, |
| reverseAlertThreshold, |
| dfsScanMinBooks, |
| dfsEdgeAlertThreshold, |
| dfsReverseAlertThreshold, |
| dfsStaleAlertThreshold, |
| exchangeScanMinBooks, |
| exchangeEdgeAlertThreshold, |
| exchangeReverseAlertThreshold, |
| exchangeStaleAlertThreshold, |
| marketBoardDefaultLimit, |
| sharpEdgeAlertsEnabled, |
| staleBookAlertsEnabled, |
| reverseAlertsEnabled, |
| circaChannelId, |
| circaDailyTime, |
| circaTimeZone, |
| circaRetryMinutes, |
| circaMovementFrequencyMinutes, |
| }, |
| }; |
| } |
|
|