From ab55c1951b9746d09196e7d27bc637d9b32ddcb4 Mon Sep 17 00:00:00 2001 From: adrunkhuman <16039109+adrunkhuman@users.noreply.github.com> Date: Sun, 10 May 2026 22:18:22 +0200 Subject: [PATCH] feat: polish invited server setup --- tests/test_admin_commands.py | 19 ++++++++++++ typer_bot/commands/admin_commands.py | 30 ++++++++++++------- typer_bot/commands/admin_panel/unified.py | 5 ++++ .../commands/admin_panel/unified_actions.py | 5 ++-- 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/tests/test_admin_commands.py b/tests/test_admin_commands.py index f6c607d..253e432 100644 --- a/tests/test_admin_commands.py +++ b/tests/test_admin_commands.py @@ -105,9 +105,16 @@ def admin_cog(self, mock_bot, database): @pytest.mark.asyncio async def test_admin_panel_opens_unified_view(self, admin_cog, mock_interaction_admin): + await admin_cog.db.upsert_guild_config("111111", "987654", "123456") + member = mock_interaction_admin.guild.get_member(mock_interaction_admin.user.id) + member.roles = [MockRole("League Admin", role_id=987654)] + await admin_cog.panel.callback(admin_cog, mock_interaction_admin) assert isinstance(mock_interaction_admin.response_sent[0]["view"], UnifiedAdminPanelView) + content = mock_interaction_admin.response_sent[0]["content"] + assert "Admin role: <@&987654>" in content + assert "League channel: <#123456>" in content def test_admin_group_exposes_panel_command(self, admin_cog): assert any(command.name == "panel" for command in admin_cog.admin.commands) @@ -165,6 +172,11 @@ async def test_inline_setup_button_opens_selector_view( await setup_button.callback(mock_interaction_admin) assert isinstance(mock_interaction_admin.response_sent[-1]["view"], GuildSetupPromptView) + assert "this server's league" in mock_interaction_admin.response_sent[-1]["content"] + assert ( + "Fixtures, reminders, results, and standings" + in mock_interaction_admin.response_sent[-1]["content"] + ) @pytest.mark.asyncio async def test_configured_panel_setup_button_opens_reconfigure_flow( @@ -183,6 +195,9 @@ async def test_configured_panel_setup_button_opens_reconfigure_flow( await setup_button.callback(mock_interaction_admin) assert isinstance(mock_interaction_admin.response_sent[-1]["view"], GuildSetupPromptView) + assert ( + "admin role and league channel" in mock_interaction_admin.response_sent[-1]["content"] + ) @pytest.mark.asyncio async def test_inline_setup_button_blocks_owner_without_setup_permission( @@ -200,6 +215,9 @@ async def test_inline_setup_button_blocks_owner_without_setup_permission( await setup_button.callback(mock_interaction_admin) assert mock_interaction_admin.response_sent[-1].get("view") is None + assert ( + "Administrator or Manage Server" in mock_interaction_admin.response_sent[-1]["content"] + ) @pytest.mark.asyncio async def test_inline_setup_selector_rechecks_setup_permission( @@ -234,6 +252,7 @@ async def test_inline_setup_prompt_saves_config( config = await database.get_guild_config("111111") assert config["admin_role_id"] == "987654" assert config["league_channel_id"] == "765432" + assert "this server's league" in mock_interaction_admin.response_sent[-1]["content"] @pytest.mark.asyncio async def test_inline_setup_prompt_requires_confirmation_for_everyone_role( diff --git a/typer_bot/commands/admin_commands.py b/typer_bot/commands/admin_commands.py index 011ff4c..e21dc95 100644 --- a/typer_bot/commands/admin_commands.py +++ b/typer_bot/commands/admin_commands.py @@ -54,7 +54,21 @@ async def _save_guild_config( def _setup_saved_message(admin_role: discord.Role, league_channel: discord.TextChannel) -> str: - return f"TyperBot setup saved. Admin role: {admin_role.mention}. League channel: {league_channel.mention}." + return ( + "TyperBot setup saved for this server's league. " + f"Admin role: {admin_role.mention}. League channel: {league_channel.mention}." + ) + + +def _setup_permission_message() -> str: + return "Only someone with Administrator or Manage Server can configure TyperBot." + + +def _setup_prompt_message() -> str: + return ( + "Choose the TyperBot admin role and league channel for this server's league. " + "Fixtures, reminders, results, and standings will be posted there." + ) class EveryoneRoleConfirmView(discord.ui.View): @@ -78,9 +92,7 @@ async def interaction_check(self, interaction: discord.Interaction) -> bool: ) return False if not has_setup_permission(interaction): - await interaction.response.send_message( - "Only a server admin can configure TyperBot for this server.", ephemeral=True - ) + await interaction.response.send_message(_setup_permission_message(), ephemeral=True) return False return True @@ -191,9 +203,7 @@ async def interaction_check(self, interaction: discord.Interaction) -> bool: ) return False if not has_setup_permission(interaction): - await interaction.response.send_message( - "Only a server admin can configure TyperBot for this server.", ephemeral=True - ) + await interaction.response.send_message(_setup_permission_message(), ephemeral=True) return False return True @@ -208,13 +218,11 @@ def __init__(self, parent_view: GuildSetupStartView): async def callback(self, interaction: discord.Interaction): if not has_setup_permission(interaction): - await interaction.response.send_message( - "Only a server admin can configure TyperBot for this server.", ephemeral=True - ) + await interaction.response.send_message(_setup_permission_message(), ephemeral=True) return await interaction.response.edit_message( - content="Choose the TyperBot admin role and league channel below.", + content=_setup_prompt_message(), view=GuildSetupPromptView(self.parent_view.db, str(interaction.user.id)), ) diff --git a/typer_bot/commands/admin_panel/unified.py b/typer_bot/commands/admin_panel/unified.py index ddb8fc0..8a658b4 100644 --- a/typer_bot/commands/admin_panel/unified.py +++ b/typer_bot/commands/admin_panel/unified.py @@ -98,6 +98,7 @@ def __init__( self.active_season_has_scores = False self.current_prediction: dict | None = None self.active_season: dict | None = None + self.guild_config: dict | None = None self.fixture_select = FixtureSelect(self) self.user_select = PredictionUserSelect(self) self.user_select.update_options([]) @@ -152,6 +153,7 @@ def _refresh_items(self) -> None: self.add_item(SetupBotButton(self, row=4)) async def load_fixture_options(self) -> None: + self.guild_config = await self.db.get_guild_config(self.guild_id) self.active_season = await self.db.get_or_create_active_season(self.guild_id) self.active_season_has_scores = await self.db.active_season_has_scores(self.guild_id) fixtures = await self.db.get_recent_fixtures(self.guild_id, MAX_SELECT_OPTIONS) @@ -197,6 +199,9 @@ async def populate_fixture_details(self, fixture: dict | None) -> None: def render_content(self) -> str: lines = ["**Admin Panel**"] + if self.guild_config is not None: + lines.append(f"Admin role: <@&{self.guild_config['admin_role_id']}>") + lines.append(f"League channel: <#{self.guild_config['league_channel_id']}>") if self.active_season is not None: lines.append(f"Active season: {self.active_season['name']}") lines.append(_format_scoring_rules(self.active_season["scoring_rules"])) diff --git a/typer_bot/commands/admin_panel/unified_actions.py b/typer_bot/commands/admin_panel/unified_actions.py index 809b32e..00a1682 100644 --- a/typer_bot/commands/admin_panel/unified_actions.py +++ b/typer_bot/commands/admin_panel/unified_actions.py @@ -24,12 +24,13 @@ async def callback(self, interaction: discord.Interaction): if not has_setup_permission(interaction): await interaction.response.send_message( - "Only a server admin can configure TyperBot for this server.", ephemeral=True + "Only someone with Administrator or Manage Server can configure TyperBot.", + ephemeral=True, ) return await interaction.response.send_message( - "Update TyperBot setup for this server.", + "Update the admin role and league channel for this server's league.", view=GuildSetupPromptView(self.parent_view.db, str(interaction.user.id)), ephemeral=True, )