rewrite the 10-man team draft as one message

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
leanderandClaude Opus 5.5 committed 2026-10-29 22:45:39 +01:00
1 parent 03b79ed98a
commit 1d29222fa4
1 file changed
+252 -180
+252 -180
View File
@@ -1,8 +1,6 @@
"""
WARNING:
This code is an absolute hot steaming pile of garbage.
My excuse is just wanting things to work, for the most part, for now.
Below you will see a lot of stupid shit. Pls ignore
10 man lobbies. The host opens a lobby and picks two team leaders, who take turns picking players for their teams.
Every step happens in the lobby message, which is edited as the flow goes on
"""
import random
@@ -53,12 +51,20 @@ class TenMan(commands.Cog):
class TenManView(LobbyView):
def __init__(self, lobby: Lobby, bot: commands.Bot):
super().__init__(lobby, bot)
"""The lobby players join before the teams are picked"""
@discord.ui.button(label="Start", style=discord.ButtonStyle.green)
@override
async def start_lobby(self, interaction: discord.Interaction, button: discord.ui.Button):
"""
Close the lobby and let the host pick team leaders
Parameters
----------
interaction (discord.Interaction): The interaction object
button (discord.ui.Button): The button that was pressed
"""
if interaction.user != self.lobby.host:
embed = embed_templates.error_warning("Bare hosten kan starte")
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
@@ -69,221 +75,287 @@ class TenManView(LobbyView):
await self.end_lobby()
embed = interaction.message.embeds[0]
embed.description = "Lobby startet! Dere skal nå velge lagledere"
await interaction.message.edit(embed=embed, view=self)
embed = discord.Embed(title="10 Man Lobby - Velg lagledere")
view = TeamLeaderView(self.lobby, self.bot)
await interaction.response.send_message(embed=embed, view=view)
view.message = await interaction.original_response()
view = TeamLeaderView(self.lobby, self.bot, self.message)
await interaction.response.edit_message(embed=view.embed(), view=view)
# ------------------------------------------------------------------------------
class TeamDraft:
"""Two team leaders taking turns picking players for their teams"""
def __init__(self, leaders: list[discord.Member], players: list[discord.Member]):
"""
Parameters
----------
leaders (list[discord.Member]): The two team leaders
players (list[discord.Member]): The players to pick from
"""
self.teams = [[leaders[0]], [leaders[1]]]
self.remaining = players
self.turn = random.randint(0, 1)
@property
def leaders(self) -> list[discord.Member]:
return [team[0] for team in self.teams]
@property
def done(self) -> bool:
return not self.remaining
def pick(self, player_id: int):
"""
Move a player to the team whose turn it is, and give the turn to the other team
Parameters
----------
player_id (int): The Discord ID of the picked player
"""
player = next(player for player in self.remaining if player.id == player_id)
self.remaining.remove(player)
self.teams[self.turn].append(player)
self.turn = 1 - self.turn
def embed(self) -> discord.Embed:
"""
Returns
----------
(discord.Embed): The teams so far, and whose turn it is
"""
if self.done:
embed = discord.Embed(title="10 Man - Lagene er klare", description="Lagene er valgt!")
else:
embed = discord.Embed(
title="10 Man - Velg spillere", description=f"Det er {self.leaders[self.turn].mention} sin tur"
)
for i, team in enumerate(self.teams):
embed.add_field(
name=f"Lag {i + 1}", value="\n".join(f"* {player.mention}" for player in team), inline=False
)
return embed
class TeamLeaderView(discord.ui.View):
def __init__(self, lobby: Lobby, bot: commands.Bot):
class StageView(discord.ui.View):
"""A step of the flow after the lobby has closed"""
def __init__(self, lobby: Lobby, bot: commands.Bot, message: discord.PartialMessage):
"""
Parameters
----------
lobby (Lobby): The closed lobby
bot (commands.Bot): The bot instance
message (discord.PartialMessage): The lobby message, edited with the bot's own token on timeout
"""
super().__init__(timeout=SELECTION_TIMEOUT)
self.lobby = lobby
self.bot = bot
self.message: discord.Message | None = None
self.message = message
self.add_item(TeamLeaderSelectMenu(self))
async def deny(self, interaction: discord.Interaction, reason: str) -> bool:
"""
Tell the user why they can't use this step
Parameters
----------
interaction (discord.Interaction): The interaction object
reason (str): Why the user was denied
Returns
----------
(bool): Always False, so it can be returned from interaction_check
"""
embed = embed_templates.error_warning(reason)
await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
return False
async def on_timeout(self):
self.bot.logger.info("TeamLeaderView timed out")
self.disable_interaction()
if self.message:
try:
await self.message.edit(view=self)
except discord.HTTPException:
pass # Deleted, or the interaction token used to edit it expired after 15 minutes
def disable_interaction(self):
self.bot.logger.info("TeamLeaderView disabled")
for item in self.children:
item.disabled = True
try:
await self.message.edit(view=self)
except discord.HTTPException:
pass # The message has been deleted
async def on_error(self, interaction: discord.Interaction, error: Exception, item: discord.ui.Item):
self.bot.logger.error("TeamLeaderView error", exc_info=error)
self.bot.logger.error(f"{type(self).__name__} error", exc_info=error)
embed = embed_templates.error_fatal("Oopsie woopsie, we made a fucky wucky!")
await interaction.response.send_message(embed=embed, delete_after=10)
if interaction.response.is_done():
await interaction.followup.send(embed=embed, ephemeral=True)
else:
await interaction.response.send_message(embed=embed, ephemeral=True)
class TeamLeaderSelectMenu(discord.ui.Select):
def __init__(self, parent_view: TeamLeaderView):
self.parent_view = parent_view
class TeamLeaderView(StageView):
"""The host picks two team leaders"""
def __init__(self, lobby: Lobby, bot: commands.Bot, message: discord.PartialMessage):
super().__init__(lobby, bot, message)
self.add_item(TeamLeaderSelect(lobby.players))
async def interaction_check(self, interaction: discord.Interaction) -> bool:
if interaction.user != self.lobby.host:
return await self.deny(interaction, "Bare hosten kan velge lagledere")
return True
def embed(self) -> discord.Embed:
"""
Returns
----------
(discord.Embed): The players to pick team leaders from
"""
embed = discord.Embed(
title="10 Man - Velg lagledere", description=f"{self.lobby.host.mention} velger lagledere"
)
embed.add_field(
name=f"Spillere ({len(self.lobby.players)})",
value="\n".join(f"* {player.mention}" for player in self.lobby.players),
)
return embed
class TeamLeaderSelect(discord.ui.Select):
def __init__(self, players: list[discord.Member]):
"""
Parameters
----------
players (list[discord.Member]): The players to pick team leaders from
"""
options = [
discord.SelectOption(label=p.display_name, value=str(p.id), emoji="🧑‍🦲", description=p.global_name)
for p in self.parent_view.lobby.players
for p in players
]
super().__init__(placeholder="Velg 2 lagledere", max_values=2, min_values=2, options=options)
super().__init__(placeholder="Velg 2 lagledere", min_values=2, max_values=2, options=options)
async def callback(self, interaction: discord.Interaction):
if interaction.user != self.parent_view.lobby.host:
embed = embed_templates.error_warning("Bare hosten kan velge lagledere")
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
"""
Start the draft with the picked team leaders
team_leaders = []
for selected_player in interaction.data["values"]:
for player in self.parent_view.lobby.players:
if int(selected_player) == player.id:
team_leaders.append(player)
break
Parameters
----------
interaction (discord.Interaction): The interaction object
"""
self.parent_view.disable_interaction()
embed = interaction.message.embeds[0]
embed.description = "Lobbyledere har blitt valgt"
embed.add_field(name="Lag 1", value=team_leaders[0].mention)
embed.add_field(name="Lag 2", value=team_leaders[1].mention)
await interaction.message.edit(embed=embed, view=self.parent_view)
view: TeamLeaderView = self.view
leaders = [player for player in view.lobby.players if str(player.id) in self.values]
others = [player for player in view.lobby.players if player not in leaders]
draft = TeamDraft(leaders, others)
turn = random.randint(0, 1)
embed = discord.Embed(title="10 Man Lobby - Velg spillere")
embed.description = f"Det er {team_leaders[turn].mention} sin tur"
embed.add_field(name="Lag 1", value=f"* {team_leaders[0].mention}", inline=False)
embed.add_field(name="Lag 2", value=f"* {team_leaders[1].mention}", inline=False)
view = TeamSelectView(self.parent_view.lobby, self.parent_view.bot, team_leaders, turn)
await interaction.response.send_message(embed=embed, view=view)
view.message = await interaction.original_response()
view.stop()
await interaction.response.edit_message(embed=draft.embed(), view=DraftView(view, draft))
# ------------------------------------------------------------------------------
class DraftView(StageView):
"""The team leaders take turns picking players"""
def __init__(self, previous: StageView, draft: TeamDraft):
"""
Parameters
----------
previous (StageView): The step before this one
draft (TeamDraft): The draft so far
"""
super().__init__(previous.lobby, previous.bot, previous.message)
self.draft = draft
self.add_item(PlayerSelect(draft.remaining))
async def interaction_check(self, interaction: discord.Interaction) -> bool:
if interaction.user not in self.draft.leaders:
return await self.deny(interaction, "Bare lagledere kan velge spillere")
if interaction.user != self.draft.leaders[self.draft.turn]:
return await self.deny(interaction, "Det er ikke din tur enda")
return True
class TeamSelectView(discord.ui.View):
def __init__(self, lobby: Lobby, bot: commands.Bot, team_leaders: list[discord.User], turn: int):
super().__init__(timeout=SELECTION_TIMEOUT)
self.lobby = lobby
self.bot = bot
self.players = [p for p in self.lobby.players if p not in team_leaders]
self.teams = [[team_leaders[0]], [team_leaders[1]]]
self.turn = turn
self.message: discord.Message | None = None
class PlayerSelect(discord.ui.Select):
def __init__(self, players: list[discord.Member]):
"""
Parameters
----------
players (list[discord.Member]): The players that haven't been picked yet
"""
self.add_item(TeamSelectMenu(self))
async def on_timeout(self):
self.bot.logger.info("TeamSelectView timed out")
self.disable_interaction()
if self.message:
try:
await self.message.edit(view=self)
except discord.HTTPException:
pass # Deleted, or the interaction token used to edit it expired after 15 minutes
def disable_interaction(self):
self.bot.logger.info("TeamSelectView disabled")
for item in self.children:
item.disabled = True
async def on_error(self, interaction: discord.Interaction, error: Exception, item: discord.ui.Item):
self.bot.logger.error("TeamSelectView error", exc_info=error)
embed = embed_templates.error_warning("Oopsie woopsie, we made a fucky wucky")
await interaction.response.send_message(embed=embed, delete_after=10)
def teams_ready(self, interaction):
self.bot.logger.info("Teams ready!")
embed = interaction.message.embeds[0]
embed.title = "10 Man Lobby"
embed.description = "Lagene er valgt!"
self.add_item(MoveTeamVoiceButton(self))
class TeamSelectMenu(discord.ui.Select):
def __init__(self, parent_view: TeamSelectView):
self.parent_view = parent_view
options = [
discord.SelectOption(label=p.display_name, value=str(p.id), emoji="🤼‍♂️", description=p.global_name)
for p in self.parent_view.players
for p in players
]
super().__init__(placeholder="Velg en spiller til laget ditt", max_values=1, min_values=1, options=options)
async def rerender_players(self, interaction: discord.Interaction, turn_in_question: int):
self.options = [
discord.SelectOption(label=p.display_name, value=str(p.id), emoji="🤼‍♂️", description=p.global_name)
for p in self.parent_view.players
]
embed = interaction.message.embeds[0]
embed.set_field_at(
turn_in_question,
name=f"Lag {turn_in_question + 1}",
value="\n".join([f"* {p.mention}" for p in self.parent_view.teams[turn_in_question]]),
)
if not self.options:
self.parent_view.clear_items()
self.parent_view.teams_ready(interaction)
else:
embed.description = f"Det er {self.parent_view.teams[self.parent_view.turn][0].mention} sin tur"
await interaction.message.edit(embed=embed, view=self.parent_view)
super().__init__(placeholder="Velg en spiller til laget ditt", options=options)
async def callback(self, interaction: discord.Interaction):
if interaction.user != self.parent_view.teams[0][0] and interaction.user != self.parent_view.teams[1][0]:
embed = embed_templates.error_warning("Bare lagledere kan velge spillere")
"""
Add the picked player to the team, and move on to voice once everyone is picked
Parameters
----------
interaction (discord.Interaction): The interaction object
"""
view: DraftView = self.view
view.draft.pick(int(self.values[0]))
next_view = VoiceView(view, view.draft.teams) if view.draft.done else DraftView(view, view.draft)
view.stop()
await interaction.response.edit_message(embed=view.draft.embed(), view=next_view)
class VoiceView(StageView):
"""Each team leader can create a voice channel for their team and move the team there"""
def __init__(self, previous: StageView, teams: list[list[discord.Member]]):
"""
Parameters
----------
previous (StageView): The step before this one
teams (list[list[discord.Member]]): The teams. The first player of each team is its leader
"""
super().__init__(previous.lobby, previous.bot, previous.message)
self.teams = teams
self.moved_teams: set[int] = set()
async def interaction_check(self, interaction: discord.Interaction) -> bool:
if interaction.user not in (self.teams[0][0], self.teams[1][0]):
return await self.deny(interaction, "Bare lagleder kan flytte laget!")
return True
@discord.ui.button(label="Opprett kanal og flytt lag", emoji="🔊", style=discord.ButtonStyle.primary)
async def move_team(self, interaction: discord.Interaction, button: discord.ui.Button):
"""
Create a temporary voice channel for the leader's team and move the team there
Parameters
----------
interaction (discord.Interaction): The interaction object
button (discord.ui.Button): The button that was pressed
"""
team = 0 if interaction.user == self.teams[0][0] else 1
if team in self.moved_teams:
embed = embed_templates.error_warning(f"Kanal for lag {team + 1} allerede opprettet og spillere flyttet!")
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
if interaction.user != self.parent_view.teams[self.parent_view.turn][0]:
embed = embed_templates.error_warning("Det er ikke din tur enda")
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
turn_in_question = self.parent_view.turn
self.parent_view.turn = 0 if self.parent_view.turn == 1 else 1
for i, player in enumerate(self.parent_view.players):
if player.id == int(interaction.data["values"][0]):
self.parent_view.players.pop(i)
self.parent_view.teams[turn_in_question].append(player)
break
await self.rerender_players(interaction, turn_in_question)
embed = embed_templates.success("Bruker valgt!")
await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=5)
# ------------------------------------------------------------------------------
class MoveTeamVoiceButton(discord.ui.Button):
def __init__(self, parent_view: TeamSelectView):
self.parent_view = parent_view
self.clicked = {"1": None, "2": None}
super().__init__(style=discord.ButtonStyle.primary, label="Opprett kanal og flytt lag", emoji="🔊")
async def callback(self, interaction: discord.Interaction):
if interaction.user != self.parent_view.teams[0][0] and interaction.user != self.parent_view.teams[1][0]:
embed = embed_templates.error_warning("Bare lagleder kan flytte laget!")
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
if interaction.user == self.parent_view.teams[0][0]:
team = {"number": "1", "players": self.parent_view.teams[0]}
else:
team = {"number": "2", "players": self.parent_view.teams[1]}
# Reuse tempvoice logic from cog
if not (tempvoice_cog := self.parent_view.bot.get_cog("TempVoice")):
if not (tempvoice_cog := self.bot.get_cog("TempVoice")):
embed = embed_templates.error_fatal("Tempvoice er ikke tilgjengelig akkurat nå")
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
temp_voice = tempvoice_cog.temp_voice_helper
if not self.clicked[team["number"]]:
channel = await temp_voice.create_temp_voice(interaction, name=f"10 man - Lag {team['number']}")
if not channel:
return
self.clicked[team["number"]] = channel
failed_users = await temp_voice.move_players(interaction, self.clicked[team["number"]], team["players"])
else:
embed = embed_templates.error_warning(
f"Kanal for lag {team['number']} allerede opprettet og spillere flyttet!"
)
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
# Mark the team before awaiting, so pressing twice quickly doesn't create two channels
self.moved_teams.add(team)
if not (channel := await temp_voice.create_temp_voice(interaction, name=f"10 man - Lag {team + 1}")):
self.moved_teams.discard(team)
return # create_temp_voice has already told the user what went wrong
if failed_users:
failed_users = "\n".join([f"* {user.mention}" for user in failed_users])
if failed_users := await temp_voice.move_players(interaction, channel, self.teams[team]):
failed_users = "\n".join(f"* {user.mention}" for user in failed_users)
embed = embed_templates.error_warning(
f"Klarte ikke å flytte følgende brukere til kanalen:\n{failed_users}\n\n"
+ "Dette kan være fordi brukere(ne) ikke er koblet til en kanal fra før "
@@ -291,7 +363,7 @@ class MoveTeamVoiceButton(discord.ui.Button):
)
return await interaction.response.send_message(embed=embed, delete_after=10)
embed = embed_templates.success(f"Flyttet lag {team['number']} til voice!")
embed = embed_templates.success(f"Flyttet lag {team + 1} til voice!")
await interaction.response.send_message(embed=embed, delete_after=10)