paper-dynasty-discord/cogs/players_new/gauntlet.py
Cal Corum 1e75f1baba CLAUDE: Add news-ticker message when gauntlet runs end with 2 losses
Users reported that end-of-run messages to #pd-news-ticker were no longer
appearing when gauntlet teams lost their second game. The news-ticker
announcement was only happening for 10-win completions, not 2-loss endings.

Changes:
- Updated end_run() to accept bot and main_team parameters
- Added send_to_channel() call when losses == 2 (natural run end)
- Skips news-ticker for manual resets (force_end=True)
- Updated post_result() to pass bot and main_team to end_run()
- Updated manual reset calls to explicitly pass force_end=True

Now when a gauntlet team loses their second game, #pd-news-ticker will
show: "The **[Team]** have completed their **[Event]** Gauntlet run
with a final record of [wins]-[losses]."

The draft completion message to news-ticker was already working correctly
at cogs/players_new/gauntlet.py:178-183.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-10 09:17:55 -06:00

240 lines
11 KiB
Python

# Gauntlet Module
# Contains gauntlet game mode functionality from the original players.py
from discord.ext import commands
from discord import app_commands
import discord
from typing import Optional
# Import specific utilities needed by this module
import logging
import datetime
from sqlmodel import Session
from api_calls import db_get, db_post, db_patch, db_delete, get_team_by_abbrev
from helpers import (
ACTIVE_EVENT_LITERAL, PD_PLAYERS_ROLE_NAME, get_team_embed, get_team_by_owner,
legal_channel, Confirm, send_to_channel
)
from helpers.utils import get_roster_sheet, get_cal_user
from utilities.buttons import ask_with_buttons
from in_game.gameplay_models import engine
from in_game.gameplay_queries import get_team_or_none
logger = logging.getLogger('discord_app')
# Try to import gauntlets module, provide fallback if not available
try:
import gauntlets
GAUNTLETS_AVAILABLE = True
except ImportError:
logger.warning("Gauntlets module not available - gauntlet commands will have limited functionality")
GAUNTLETS_AVAILABLE = False
gauntlets = None
class Gauntlet(commands.Cog):
"""Gauntlet game mode functionality for Paper Dynasty."""
def __init__(self, bot):
self.bot = bot
group_gauntlet = app_commands.Group(name='gauntlets', description='Check your progress or start a new Gauntlet')
@group_gauntlet.command(name='status', description='View status of current Gauntlet run')
@app_commands.describe(
team_abbrev='To check the status of a team\'s active run, enter their abbreviation'
)
@app_commands.checks.has_any_role(PD_PLAYERS_ROLE_NAME)
async def gauntlet_run_command(
self, interaction: discord.Interaction, event_name: ACTIVE_EVENT_LITERAL, # type: ignore
team_abbrev: Optional[str] = None):
"""View status of current gauntlet run - corrected to match original business logic."""
await interaction.response.defer()
e_query = await db_get('events', params=[("name", event_name), ("active", True)])
if not e_query or e_query.get('count', 0) == 0:
await interaction.edit_original_response(content=f'Hmm...looks like that event is inactive.')
return
else:
this_event = e_query['events'][0]
this_run, this_team = None, None
if team_abbrev:
if 'Gauntlet-' not in team_abbrev:
team_abbrev = f'Gauntlet-{team_abbrev}'
t_query = await db_get('teams', params=[('abbrev', team_abbrev)])
if t_query and t_query.get('count', 0) != 0:
this_team = t_query['teams'][0]
r_query = await db_get('gauntletruns', params=[
('team_id', this_team['id']), ('is_active', True), ('gauntlet_id', this_event['id'])
])
if r_query and r_query.get('count', 0) != 0:
this_run = r_query['runs'][0]
else:
await interaction.edit_original_response(
content=f'I do not see an active run for the {this_team["lname"]}.'
)
return
else:
await interaction.edit_original_response(
content=f'I do not see an active run for {team_abbrev.upper()}.'
)
return
# Use gauntlets module if available, otherwise show error
if GAUNTLETS_AVAILABLE and gauntlets:
await interaction.edit_original_response(
content=None,
embed=await gauntlets.get_embed(this_run, this_event, this_team) # type: ignore
)
else:
await interaction.edit_original_response(
content='Gauntlet status unavailable - gauntlets module not loaded.'
)
@group_gauntlet.command(name='start', description='Start a new Gauntlet run')
@app_commands.checks.has_any_role(PD_PLAYERS_ROLE_NAME)
async def gauntlet_start_command(self, interaction: discord.Interaction):
"""Start a new gauntlet run."""
# Channel restriction - must be in a 'hello' channel (private channel)
if interaction.channel and hasattr(interaction.channel, 'name') and 'hello' not in str(interaction.channel.name):
await interaction.response.send_message(
content='The draft will probably take you about 15 minutes. Why don\'t you head to your private '
'channel to run the draft?',
ephemeral=True
)
return
logger.info(f'Starting a gauntlet run for user {interaction.user.name}')
await interaction.response.defer()
with Session(engine) as session:
main_team = await get_team_or_none(session, gm_id=interaction.user.id, main_team=True)
draft_team = await get_team_or_none(session, gm_id=interaction.user.id, gauntlet_team=True)
# Get active events
e_query = await db_get('events', params=[("active", True)])
if not e_query or e_query.get('count', 0) == 0:
await interaction.edit_original_response(content='Hmm...I don\'t see any active events.')
return
elif e_query.get('count', 0) == 1:
this_event = e_query['events'][0]
else:
event_choice = await ask_with_buttons(
interaction,
button_options=[x['name'] for x in e_query['events']],
question='Which event would you like to take on?',
timeout=3,
delete_question=False
)
this_event = [event for event in e_query['events'] if event['name'] == event_choice][0]
logger.info(f'this_event: {this_event}')
first_flag = draft_team is None
if draft_team is not None:
r_query = await db_get(
'gauntletruns',
params=[('team_id', draft_team.id), ('gauntlet_id', this_event['id']), ('is_active', True)]
)
if r_query and r_query.get('count', 0) != 0:
await interaction.edit_original_response(
content=f'Looks like you already have a {r_query["runs"][0]["gauntlet"]["name"]} run active! '
f'You can check it out with the `/gauntlets status` command.'
)
return
try:
draft_embed = await gauntlets.run_draft(interaction, main_team, this_event, draft_team) # type: ignore
except ZeroDivisionError as e:
return
except Exception as e:
logger.error(f'Failed to run {this_event["name"]} draft for the {main_team.sname if main_team else "unknown"}: {e}')
await gauntlets.wipe_team(draft_team, interaction) # type: ignore
await interaction.followup.send(
content=f'Shoot - it looks like we ran into an issue running the draft. I had to clear it all out '
f'for now. I let {get_cal_user(interaction).mention} know what happened so he better '
f'fix it quick.'
)
return
if first_flag:
await interaction.followup.send(
f'Good luck, champ in the making! To start playing, follow these steps:\n\n'
f'1) Make a copy of the Team Sheet Template found in `/help-pd links`\n'
f'2) Run `/newsheet` to link it to your Gauntlet team\n'
f'3) Go play your first game with `/new-game gauntlet {this_event["name"]}`'
)
else:
await interaction.followup.send(
f'Good luck, champ in the making! In your team sheet, sync your cards with **Paper Dynasty** -> '
f'**Data Imports** -> **My Cards** then you can set your lineup here and you\'ll be ready to go!\n\n'
f'{get_roster_sheet(draft_team)}'
)
await send_to_channel(
bot=self.bot,
channel_name='pd-news-ticker',
content=f'The {main_team.lname if main_team else "Unknown Team"} have entered the {this_event["name"]} Gauntlet!',
embed=draft_embed
)
@group_gauntlet.command(name='reset', description='Wipe your current team so you can re-draft')
@app_commands.checks.has_any_role(PD_PLAYERS_ROLE_NAME)
async def gauntlet_reset_command(self, interaction: discord.Interaction, event_name: ACTIVE_EVENT_LITERAL): # type: ignore
"""Reset current gauntlet run."""
await interaction.response.defer()
main_team = await get_team_by_owner(interaction.user.id)
draft_team = await get_team_by_abbrev(f'Gauntlet-{main_team["abbrev"]}')
if draft_team is None:
await interaction.edit_original_response(
content='Hmm, I can\'t find a gauntlet team for you. Have you signed up already?')
return
e_query = await db_get('events', params=[("name", event_name), ("active", True)])
if e_query['count'] == 0:
await interaction.edit_original_response(content='Hmm...looks like that event is inactive.')
return
else:
this_event = e_query['events'][0]
r_query = await db_get('gauntletruns', params=[
('team_id', draft_team['id']), ('is_active', True), ('gauntlet_id', this_event['id'])
])
if r_query and r_query.get('count', 0) != 0:
this_run = r_query['runs'][0]
else:
await interaction.edit_original_response(
content=f'I do not see an active run for the {draft_team["lname"]}.'
)
return
view = Confirm(responders=[interaction.user], timeout=60)
conf_string = f'Are you sure you want to wipe your active run?'
await interaction.edit_original_response(
content=conf_string,
view=view
)
await view.wait()
if view.value:
await gauntlets.end_run(this_run, this_event, draft_team, force_end=True) # type: ignore
await interaction.edit_original_response(
content=f'Your {event_name} run has been reset. Run `/gauntlets start` to redraft!',
view=None
)
else:
await interaction.edit_original_response(
content=f'~~{conf_string}~~\n\nNo worries, I will leave it active.',
view=None
)
async def setup(bot):
"""Setup function for the Gauntlet cog."""
await bot.add_cog(Gauntlet(bot))