ROIBot / src /config.js
Codex
Tighten lane minimum book thresholds
d1c5d0f
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,
},
};
}