feat: validate bot voice channel permissions
This commit is contained in:
6 files changed
+60
-2
No files matched your search
@@ -102,7 +102,7 @@ async function saveDefaultPlays() {
|
|||||||
:key="channel.channel_id"
|
:key="channel.channel_id"
|
||||||
:value="channel.channel_id"
|
:value="channel.channel_id"
|
||||||
>
|
>
|
||||||
{{ channel.guild_name }} - {{ channel.channel_name }}
|
{{ channel.channel_name }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -86,6 +86,37 @@ class ApiSurfaceTest(unittest.TestCase):
|
|||||||
self.assertEqual(result, {"status": "restarting"})
|
self.assertEqual(result, {"status": "restarting"})
|
||||||
mock_restart.assert_called_once()
|
mock_restart.assert_called_once()
|
||||||
|
|
||||||
|
def test_voice_channels_payload_filters_unconnectable_channels(self):
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
from zyrarbot.ui.api.status_service import StatusService
|
||||||
|
|
||||||
|
mock_guild = MagicMock()
|
||||||
|
mock_guild.id = 123
|
||||||
|
mock_guild.name = "Test Guild"
|
||||||
|
|
||||||
|
ch1 = MagicMock()
|
||||||
|
ch1.id = 1
|
||||||
|
ch1.name = "Allowed Voice"
|
||||||
|
ch1.permissions_for.return_value.connect = True
|
||||||
|
|
||||||
|
ch2 = MagicMock()
|
||||||
|
ch2.id = 2
|
||||||
|
ch2.name = "Forbidden Voice"
|
||||||
|
ch2.permissions_for.return_value.connect = False
|
||||||
|
|
||||||
|
mock_guild.voice_channels = [ch1, ch2]
|
||||||
|
mock_guild.me = MagicMock()
|
||||||
|
|
||||||
|
mock_guilds_resolver = MagicMock()
|
||||||
|
mock_guilds_resolver.guilds.return_value = [mock_guild]
|
||||||
|
|
||||||
|
service = StatusService(MagicMock(), mock_guilds_resolver)
|
||||||
|
channels = service._voice_channels_payload(mock_guild)
|
||||||
|
|
||||||
|
self.assertEqual(len(channels), 1)
|
||||||
|
self.assertEqual(channels[0]["channel_id"], "1")
|
||||||
|
self.assertEqual(channels[0]["channel_name"], "Allowed Voice")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
@@ -63,6 +63,12 @@ async def connect(
|
|||||||
if not isinstance(channel, discord.VoiceChannel):
|
if not isinstance(channel, discord.VoiceChannel):
|
||||||
raise HTTPException(status_code=404, detail="Voice channel not found")
|
raise HTTPException(status_code=404, detail="Voice channel not found")
|
||||||
|
|
||||||
|
if channel.guild.me and not channel.permissions_for(channel.guild.me).connect:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=403,
|
||||||
|
detail=f"Bot nie ma uprawnień do połączenia z kanałem {channel.name}",
|
||||||
|
)
|
||||||
|
|
||||||
guild_id = channel.guild.id
|
guild_id = channel.guild.id
|
||||||
# Rozłączamy wyłącznie na tym samym serwerze - inne serwery grają dalej.
|
# Rozłączamy wyłącznie na tym samym serwerze - inne serwery grają dalej.
|
||||||
existing = guilds.voice_client(guild_id)
|
existing = guilds.voice_client(guild_id)
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ class StatusService:
|
|||||||
for guild in self._guilds.guilds()
|
for guild in self._guilds.guilds()
|
||||||
if selected_guild is None or guild.id == selected_guild.id
|
if selected_guild is None or guild.id == selected_guild.id
|
||||||
for channel in guild.voice_channels
|
for channel in guild.voice_channels
|
||||||
|
if guild.me and channel.permissions_for(guild.me).connect
|
||||||
]
|
]
|
||||||
|
|
||||||
def _guilds_payload(self) -> list[dict[str, Any]]:
|
def _guilds_payload(self) -> list[dict[str, Any]]:
|
||||||
|
|||||||
@@ -24,7 +24,17 @@ class MusicCogBase(commands.Cog):
|
|||||||
|
|
||||||
voice_client = interaction.guild.voice_client
|
voice_client = interaction.guild.voice_client
|
||||||
if voice_client is None:
|
if voice_client is None:
|
||||||
await interaction.user.voice.channel.connect()
|
user_channel = interaction.user.voice.channel
|
||||||
|
if interaction.guild.me and not user_channel.permissions_for(interaction.guild.me).connect:
|
||||||
|
await interaction.response.send_message(
|
||||||
|
embed=message_embed(
|
||||||
|
"Brak uprawnień",
|
||||||
|
f"Bot nie ma uprawnień do połączenia z kanałem **{user_channel.name}**.",
|
||||||
|
),
|
||||||
|
ephemeral=True,
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
await user_channel.connect()
|
||||||
elif voice_client.channel != interaction.user.voice.channel:
|
elif voice_client.channel != interaction.user.voice.channel:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
embed=message_embed(
|
embed=message_embed(
|
||||||
|
|||||||
@@ -34,6 +34,16 @@ class VoiceCog(MusicCogBase, name="Voice"):
|
|||||||
async def connect(
|
async def connect(
|
||||||
self, interaction: discord.Interaction, channel: discord.VoiceChannel
|
self, interaction: discord.Interaction, channel: discord.VoiceChannel
|
||||||
) -> None:
|
) -> None:
|
||||||
|
if interaction.guild.me and not channel.permissions_for(interaction.guild.me).connect:
|
||||||
|
await interaction.response.send_message(
|
||||||
|
embed=message_embed(
|
||||||
|
"Brak uprawnień",
|
||||||
|
f"Bot nie ma uprawnień do połączenia z kanałem **{channel.name}**.",
|
||||||
|
),
|
||||||
|
ephemeral=True,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
existing = self.player.voice_client(interaction.guild.id)
|
existing = self.player.voice_client(interaction.guild.id)
|
||||||
if existing:
|
if existing:
|
||||||
await existing.disconnect()
|
await existing.disconnect()
|
||||||
|
|||||||
Reference in new issue
Block a user