mirror of
https://github.com/UiO-Gaming/mustafa.git
synced 2026-11-05 13:56:48 +01:00
rewrite the 10-man team draft as one message
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
03b79ed98a
commit
1d29222fa4
1 file changed
+252
-180
+252
-180
@@ -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)
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user