Skip to content

Commit

Permalink
feat: handle connecting and disconnecting properly
Browse files Browse the repository at this point in the history
  • Loading branch information
ooliver1 committed Sep 18, 2022
1 parent 68621aa commit 38ea309
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 20 deletions.
11 changes: 7 additions & 4 deletions mafic/__libraries.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
"Client",
"Connectable",
"ExponentialBackoff",
"Guild",
"GuildChannel",
"GuildVoiceStatePayload",
"StageChannel",
"VoiceChannel",
"VoiceProtocol",
"VoiceServerUpdatePayload",
"dumps",
Expand Down Expand Up @@ -47,7 +50,7 @@
simplefilter("ignore", RuntimeWarning)
from nextcord.health_check import DistributionWarning

simplefilter("ignore", RuntimeWarning)
simplefilter("always", RuntimeWarning)

simplefilter("ignore", DistributionWarning)

Expand All @@ -56,23 +59,23 @@


if library == "nextcord":
from nextcord import Client, VoiceProtocol
from nextcord import Client, Guild, StageChannel, VoiceChannel, VoiceProtocol
from nextcord.abc import Connectable, GuildChannel
from nextcord.backoff import ExponentialBackoff
from nextcord.types.voice import (
GuildVoiceState as GuildVoiceStatePayload,
VoiceServerUpdate as VoiceServerUpdatePayload,
)
elif library == "disnake":
from disnake import Client, VoiceProtocol
from disnake import Client, Guild, StageChannel, VoiceChannel, VoiceProtocol
from disnake.abc import Connectable, GuildChannel
from disnake.backoff import ExponentialBackoff
from disnake.types.voice import (
GuildVoiceState as GuildVoiceStatePayload,
VoiceServerUpdate as VoiceServerUpdatePayload,
)
else:
from discord import Client, VoiceProtocol
from discord import Client, Guild, StageChannel, VoiceChannel, VoiceProtocol
from discord.abc import Connectable, GuildChannel
from discord.backoff import ExponentialBackoff
from discord.types.voice import (
Expand Down
19 changes: 16 additions & 3 deletions mafic/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,11 +229,14 @@ async def _handle_event(self, data: EventPayload) -> None:
event_type = cast(str, data["type"])
_log.warn("Unknown incoming event type %s", event_type)

def send_voice_server_update(
self, guild_id: int, session_id: str, data: VoiceServerUpdatePayload
def send_player_update(
self,
guild_id: int,
session_id: str,
data: VoiceServerUpdatePayload,
) -> Coro[None]:
_log.debug(
"Sending voice server update to lavalink with data %s.",
"Sending player update to lavalink with data %s.",
data,
extra={"label": self._label, "guild": guild_id},
)
Expand Down Expand Up @@ -261,3 +264,13 @@ def send_resume_configuration(self) -> Coro[None]:
"timeout": 60,
}
)

def destroy(self, guild_id: int) -> Coro[None]:
_log.debug("Sending request to destroy player", extra={"label": self._label})

return self.__send(
{
"op": "destroy",
"guildId": str(guild_id),
}
)
89 changes: 76 additions & 13 deletions mafic/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
from logging import getLogger
from typing import TYPE_CHECKING

from .__libraries import VoiceProtocol
from .__libraries import GuildChannel, StageChannel, VoiceChannel, VoiceProtocol
from .pool import NodePool

if TYPE_CHECKING:
from .__libraries import (
Client,
Connectable,
GuildChannel,
Guild,
GuildVoiceStatePayload,
VoiceServerUpdatePayload,
)
Expand All @@ -32,14 +32,60 @@ def __init__(
) -> None:
self.client: Client = client
self.channel: Connectable = channel

if not isinstance(self.channel, GuildChannel):
raise TypeError("Voice channel must be a GuildChannel.")

self.guild: Guild = self.channel.guild

self._node = node

self._guild_id: int | None = None
self._guild_id: int = self.guild.id
self._session_id: str | None = None
self._server_state: VoiceServerUpdatePayload | None = None
self._connected: bool = False

@property
def connected(self) -> bool:
return self._connected

# If people are so in love with the VoiceClient interface
def is_connected(self):
return self._connected

async def _dispatch_player_update(
self, data: GuildVoiceStatePayload | VoiceServerUpdatePayload
) -> None:
if self._node is None:
_log.debug("Recieved voice update before node was found.")
return

if self._session_id is None:
_log.debug("Receieved player update before session ID was set.")
return

if self._server_state is None:
_log.debug("Receieved player update before server state was found")
return

await self._node.send_player_update(
guild_id=self._guild_id,
session_id=self._session_id,
data=self._server_state,
)

async def on_voice_state_update(self, data: GuildVoiceStatePayload) -> None:
raise NotImplementedError
before_session_id = self._session_id
self._session_id = data["session_id"]

channel_id = data["channel_id"]
channel = self.guild.get_channel(int(channel_id))
assert isinstance(channel, (StageChannel, VoiceChannel))

self.channel = channel

if self._session_id != before_session_id:
await self._dispatch_player_update(data)

async def on_voice_server_update(self, data: VoiceServerUpdatePayload) -> None:
if self._node is None:
Expand All @@ -48,15 +94,12 @@ async def on_voice_server_update(self, data: VoiceServerUpdatePayload) -> None:
guild_id=data["guild_id"], endpoint=data["endpoint"]
)

self._server_state = data
self._node.players[self._guild_id] = self

if self._guild_id is None or self._session_id is None:
_log.warn("Ignoring voice server update as guild and session are unknown.")
return
self._guild_id = int(data["guild_id"])
self._server_state = data

await self._node.send_voice_server_update(
guild_id=self._guild_id, session_id=self._session_id, data=data
)
await self._dispatch_player_update(data)

async def connect(
self,
Expand All @@ -74,6 +117,26 @@ async def connect(
await self.channel.guild.change_voice_state(
channel=self.channel, self_mute=self_mute, self_deaf=self_deaf
)
self._connected = True

async def disconnect(self, *, force: bool = False) -> None:
try:
_log.debug(
"Disconnecting from voice channel.",
extra={"guild": self._guild_id},
)
await self.guild.change_voice_state(channel=None)
finally:
self.cleanup()
self._connected = False

async def destroy(self) -> None:
_log.debug(
"Disconnecting player and destroying client.",
extra={"guild": self._guild_id},
)
await self.disconnect()

async def disconnect(self, *, force: bool) -> None:
self.cleanup()
if self._node is not None:
self._node.players.pop(self.guild.id, None)
await self._node.destroy(guild_id=self.guild.id)

0 comments on commit 38ea309

Please sign in to comment.