Codex commited on
Commit ·
2744367
1
Parent(s): 85681b0
Refine home run odds command defaults
Browse files- src/commands.js +10 -3
- src/embeds.js +3 -3
- src/index.js +2 -2
- src/market-scanner.js +43 -13
src/commands.js
CHANGED
|
@@ -263,11 +263,18 @@ export const commands = [
|
|
| 263 |
.setDescription('Player name to look up, for example Aaron Judge.')
|
| 264 |
.setRequired(true)
|
| 265 |
)
|
| 266 |
-
.
|
| 267 |
option
|
| 268 |
-
.setName('
|
| 269 |
-
.setDescription('
|
| 270 |
.setRequired(false)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
),
|
| 272 |
new SlashCommandBuilder()
|
| 273 |
.setName('alerts')
|
|
|
|
| 263 |
.setDescription('Player name to look up, for example Aaron Judge.')
|
| 264 |
.setRequired(true)
|
| 265 |
)
|
| 266 |
+
.addStringOption((option) =>
|
| 267 |
option
|
| 268 |
+
.setName('book')
|
| 269 |
+
.setDescription('Optionally limit the result to one book. Defaults to all books including Circa.')
|
| 270 |
.setRequired(false)
|
| 271 |
+
.addChoices(
|
| 272 |
+
{ name: 'Circa', value: 'Circa' },
|
| 273 |
+
{ name: 'FanDuel', value: 'FanDuel' },
|
| 274 |
+
{ name: 'DraftKings', value: 'DraftKings' },
|
| 275 |
+
{ name: 'BetMGM', value: 'BetMGM' },
|
| 276 |
+
{ name: 'Caesars', value: 'Caesars' }
|
| 277 |
+
)
|
| 278 |
),
|
| 279 |
new SlashCommandBuilder()
|
| 280 |
.setName('alerts')
|
src/embeds.js
CHANGED
|
@@ -306,7 +306,7 @@ export function buildCommandsEmbed() {
|
|
| 306 |
{ name: '/scanstatus', value: 'Show the market scanner status and latest run info. Admin only.' },
|
| 307 |
{ name: '/scanrun', value: 'Run the market scanner manually. Admin only.' },
|
| 308 |
{ name: '/scanreport', value: 'Post the morning scan reports immediately. Admin only.' },
|
| 309 |
-
{ name: '/hrodds', value: 'Show live Home Run odds for one player across FanDuel, DraftKings, BetMGM, Caesars, and
|
| 310 |
{ name: '/circatest', value: 'Run a Circa OCR diagnostic preview. Admin only.' },
|
| 311 |
{ name: 'Circa Market Commands', value: '`/circamarket`, `/circahr`, `/circahits`, `/circatb`, `/circarbis`, `/circaruns`, `/circasb`, `/circahrri`, `/circak` post the latest parsed Circa markets in the channel where you run them. Admin only.' },
|
| 312 |
{ name: '/alerts', value: 'Post the public analyst alert-role panel to the welcome channel. Only for jew_olympics.' },
|
|
@@ -314,11 +314,11 @@ export function buildCommandsEmbed() {
|
|
| 314 |
);
|
| 315 |
}
|
| 316 |
|
| 317 |
-
export function buildPlayerOddsEmbed({ playerName, marketLabel, entries,
|
| 318 |
const embed = new EmbedBuilder()
|
| 319 |
.setColor(PALETTE.primary)
|
| 320 |
.setTitle(`${marketLabel} Odds`)
|
| 321 |
-
.setDescription(`Current
|
| 322 |
|
| 323 |
if (!entries.length) {
|
| 324 |
return embed.setDescription(`No ${marketLabel.toLowerCase()} odds were found for **${playerName}**.`);
|
|
|
|
| 306 |
{ name: '/scanstatus', value: 'Show the market scanner status and latest run info. Admin only.' },
|
| 307 |
{ name: '/scanrun', value: 'Run the market scanner manually. Admin only.' },
|
| 308 |
{ name: '/scanreport', value: 'Post the morning scan reports immediately. Admin only.' },
|
| 309 |
+
{ name: '/hrodds', value: 'Show live Home Run odds for one player across FanDuel, DraftKings, BetMGM, Caesars, and Circa, or filter to one book. Admin only.' },
|
| 310 |
{ name: '/circatest', value: 'Run a Circa OCR diagnostic preview. Admin only.' },
|
| 311 |
{ name: 'Circa Market Commands', value: '`/circamarket`, `/circahr`, `/circahits`, `/circatb`, `/circarbis`, `/circaruns`, `/circasb`, `/circahrri`, `/circak` post the latest parsed Circa markets in the channel where you run them. Admin only.' },
|
| 312 |
{ name: '/alerts', value: 'Post the public analyst alert-role panel to the welcome channel. Only for jew_olympics.' },
|
|
|
|
| 314 |
);
|
| 315 |
}
|
| 316 |
|
| 317 |
+
export function buildPlayerOddsEmbed({ playerName, marketLabel, entries, bookFilter }) {
|
| 318 |
const embed = new EmbedBuilder()
|
| 319 |
.setColor(PALETTE.primary)
|
| 320 |
.setTitle(`${marketLabel} Odds`)
|
| 321 |
+
.setDescription(`Current ${bookFilter ? `**${bookFilter}**` : 'book'} prices for **${playerName}**.`);
|
| 322 |
|
| 323 |
if (!entries.length) {
|
| 324 |
return embed.setDescription(`No ${marketLabel.toLowerCase()} odds were found for **${playerName}**.`);
|
src/index.js
CHANGED
|
@@ -1068,8 +1068,8 @@ async function handleHomeRunOdds(interaction, config) {
|
|
| 1068 |
|
| 1069 |
try {
|
| 1070 |
const player = interaction.options.getString('player', true);
|
| 1071 |
-
const
|
| 1072 |
-
const result = await scanner.getPlayerHomeRunOdds(player, {
|
| 1073 |
await interaction.editReply({
|
| 1074 |
embeds: [buildPlayerOddsEmbed(result)],
|
| 1075 |
});
|
|
|
|
| 1068 |
|
| 1069 |
try {
|
| 1070 |
const player = interaction.options.getString('player', true);
|
| 1071 |
+
const book = interaction.options.getString('book') ?? null;
|
| 1072 |
+
const result = await scanner.getPlayerHomeRunOdds(player, { book });
|
| 1073 |
await interaction.editReply({
|
| 1074 |
embeds: [buildPlayerOddsEmbed(result)],
|
| 1075 |
});
|
src/market-scanner.js
CHANGED
|
@@ -2733,6 +2733,31 @@ function findBestPlayerEntryMatch(entries, playerQuery) {
|
|
| 2733 |
});
|
| 2734 |
}
|
| 2735 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2736 |
function normalizeCircaMarketType(marketType) {
|
| 2737 |
return CIRCA_MARKET_ALIASES[marketType] ?? marketType;
|
| 2738 |
}
|
|
@@ -3383,19 +3408,17 @@ export class MarketScanner {
|
|
| 3383 |
}
|
| 3384 |
|
| 3385 |
async getPlayerHomeRunOdds(playerQuery, options = {}) {
|
| 3386 |
-
const
|
| 3387 |
const oddsEntries = await fetchOddsApiEntries(this.config);
|
| 3388 |
const homeRunEntries = oddsEntries.filter((entry) => entry.marketType === 'batter_home_runs' && entry.side === 'yes');
|
| 3389 |
const matchedEntries = findBestPlayerEntryMatch(homeRunEntries, playerQuery);
|
| 3390 |
|
| 3391 |
-
|
| 3392 |
-
|
| 3393 |
-
|
| 3394 |
-
|
| 3395 |
-
|
| 3396 |
-
|
| 3397 |
-
matchedEntries.push(...findBestPlayerEntryMatch(circaEntries, playerQuery));
|
| 3398 |
-
}
|
| 3399 |
}
|
| 3400 |
|
| 3401 |
const dedupedEntries = dedupeBy(
|
|
@@ -3407,15 +3430,22 @@ export class MarketScanner {
|
|
| 3407 |
(entry) => `${entry.book}|${entry.marketKey}|${entry.oddsInput}`,
|
| 3408 |
);
|
| 3409 |
|
| 3410 |
-
|
| 3411 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3412 |
}
|
| 3413 |
|
| 3414 |
-
const canonicalName =
|
| 3415 |
return {
|
| 3416 |
playerName: canonicalName,
|
| 3417 |
marketLabel: 'Home Runs',
|
| 3418 |
-
entries:
|
|
|
|
| 3419 |
};
|
| 3420 |
}
|
| 3421 |
|
|
|
|
| 2733 |
});
|
| 2734 |
}
|
| 2735 |
|
| 2736 |
+
function normalizeBookFilter(book) {
|
| 2737 |
+
const normalized = normalizeWhitespace(book).toLowerCase();
|
| 2738 |
+
if (!normalized) {
|
| 2739 |
+
return null;
|
| 2740 |
+
}
|
| 2741 |
+
|
| 2742 |
+
if (normalized === 'circa') {
|
| 2743 |
+
return 'Circa';
|
| 2744 |
+
}
|
| 2745 |
+
if (normalized === 'fanduel') {
|
| 2746 |
+
return 'FanDuel';
|
| 2747 |
+
}
|
| 2748 |
+
if (normalized === 'draftkings') {
|
| 2749 |
+
return 'DraftKings';
|
| 2750 |
+
}
|
| 2751 |
+
if (normalized === 'betmgm') {
|
| 2752 |
+
return 'BetMGM';
|
| 2753 |
+
}
|
| 2754 |
+
if (normalized === 'caesars' || normalized === 'williamhill_us') {
|
| 2755 |
+
return 'Caesars';
|
| 2756 |
+
}
|
| 2757 |
+
|
| 2758 |
+
return book;
|
| 2759 |
+
}
|
| 2760 |
+
|
| 2761 |
function normalizeCircaMarketType(marketType) {
|
| 2762 |
return CIRCA_MARKET_ALIASES[marketType] ?? marketType;
|
| 2763 |
}
|
|
|
|
| 3408 |
}
|
| 3409 |
|
| 3410 |
async getPlayerHomeRunOdds(playerQuery, options = {}) {
|
| 3411 |
+
const bookFilter = normalizeBookFilter(options.book ?? '');
|
| 3412 |
const oddsEntries = await fetchOddsApiEntries(this.config);
|
| 3413 |
const homeRunEntries = oddsEntries.filter((entry) => entry.marketType === 'batter_home_runs' && entry.side === 'yes');
|
| 3414 |
const matchedEntries = findBestPlayerEntryMatch(homeRunEntries, playerQuery);
|
| 3415 |
|
| 3416 |
+
const snapshot = await this.getCircaSnapshotForAnalysis().catch(() => null);
|
| 3417 |
+
if (snapshot?.entries?.length) {
|
| 3418 |
+
const circaEntries = snapshot.entries.filter((entry) =>
|
| 3419 |
+
normalizeCircaMarketType(entry.marketType) === 'home_runs' && entry.side === 'yes'
|
| 3420 |
+
);
|
| 3421 |
+
matchedEntries.push(...findBestPlayerEntryMatch(circaEntries, playerQuery));
|
|
|
|
|
|
|
| 3422 |
}
|
| 3423 |
|
| 3424 |
const dedupedEntries = dedupeBy(
|
|
|
|
| 3430 |
(entry) => `${entry.book}|${entry.marketKey}|${entry.oddsInput}`,
|
| 3431 |
);
|
| 3432 |
|
| 3433 |
+
const filteredEntries = bookFilter
|
| 3434 |
+
? dedupedEntries.filter((entry) => normalizeBookFilter(entry.book) === bookFilter)
|
| 3435 |
+
: dedupedEntries;
|
| 3436 |
+
|
| 3437 |
+
if (filteredEntries.length === 0) {
|
| 3438 |
+
throw new Error(bookFilter
|
| 3439 |
+
? `No ${bookFilter} Home Runs odds were found for ${playerQuery}.`
|
| 3440 |
+
: `No Home Runs odds were found for ${playerQuery}.`);
|
| 3441 |
}
|
| 3442 |
|
| 3443 |
+
const canonicalName = filteredEntries[0].playerName;
|
| 3444 |
return {
|
| 3445 |
playerName: canonicalName,
|
| 3446 |
marketLabel: 'Home Runs',
|
| 3447 |
+
entries: filteredEntries,
|
| 3448 |
+
bookFilter,
|
| 3449 |
};
|
| 3450 |
}
|
| 3451 |
|